Wikipedia testwiki https://test.wikipedia.org/wiki/Main_Page MediaWiki 1.47.0-wmf.18 first-letter Media Special Talk User User talk Wikipedia Wikipedia talk File File talk MediaWiki MediaWiki talk Template Template talk Help Help talk Category Category talk Thread Thread talk Summary Summary talk Test namespace 1 Test namespace 1 talk Test namespace 2 Test namespace 2 talk Draft Draft talk Campaign Campaign talk TimedText TimedText talk Module Module talk SecurePoll SecurePoll talk Package Package talk CNBanner CNBanner talk Translations Translations talk Event Event talk Topic Newsletter Newsletter talk Wikipedia:Test 4 71460 760347 760182 2026-09-04T22:40:08Z Rosetvrnip 75253 /* Test */ Start 760347 wikitext text/x-wiki = Test = 904 Candy Crush Saga Wiki: Tasty Sugar Track Edits Is Out Of Control and continue until the 24 Restriction until no edits made all day, Most Edits made on 6/23, No New Episodes Released on 6/24. 2:6 — 8:18, 194 days, (8:19+ to fill out). *8:19, 8:20, 8:21, 8:22, 8:23 (?) *8:24, 8:25, 8:26, 8:27, 8:28 (?) *8:29 (?) *8:30 (?) *8:31 *9:1 (9:2) ''9:3'' *202 6525, Pot with Fork and Spoon, Bags with Sunglasses. *202 6526, Baskets, Tobacco Stuff or Socks. *202 6527, Food Shopping like Cola Bottles, Cans, Cordial and Others. *202 6528, Socks, Pairs of Shoes, Dressing Gowns. *202 6529, Towels, Cubes with Things. *202 6530, Cloths, Flowers Frisun or Soap. *202 6531, Food Shopping like Party Pies, Waters and Others. *202 6601, Pairs of Boots, Pillow or Podiatrist. *202 6602, Plates or Pans, Grated Cheese or Baskets. *202 6603, Forks, Pans and Other Kitchen Tools. *202 6604, Cups, Cordial Jug or Strawberry Milk. *202 6605, Food Shopping like Cans of Coca Cola, Waters, Cordials or Pile of Sauce. *202 6606, Purses, Lu Papers or Paper Towels. *202 6607, Candles, Statues or Pizza Boxes. *202 6608, Cups, Blankets and Others. *202 6609, Food Shoppibg like Cola Bottles, Sour Cream and Onion Chips or Cans. *202 6610, Towels, Cloths, Soap or Containers. *202 6611, Nachos, Apple N Raspberry Codrial or Stationary. *202 6612, Basket, Electric Bed with Instructions or Paint. *202 6613, Aerobar, Birthday Cake or Packet of Candles. *202 6614, Plates, Blankets or even Vacuum Cleaner. *202 6615, Towels, Cloths or Dressing Gowns. *202 6616, Smiths Chips, Heater Vents, Stuff for Lasagna or Cloths. *202 6617, Pans, Table Spoons or Lids. *202 6618, Food Shopping like Cans of Coca Cola, Waters, Milk and Others. *202 6619, Chocolate Milk, Cups of Tea or Extra Mints. *202 6620, Kitkats, Pens or Domino's Pizza. *202 6621, Blankets and Pillows or Vacuum Cleaner. *202 6622, Tea Towels, Cans of Coca Cola or Carlton Draught. *202 6623, Food Shopping like Bottles of Cola, Waters or New Pyjamas. *202 6624, Staples, Socks, Hats and Others. *202 6625, Kitchen Bowls, Pans and Others. *202 6626, Food Shopping like Colas, Milk or Rainbow Paddlepop. *202 6627, Tablemats, Sunglasses or Garden Plant. *202 6628, Boots, Sleep Medicine or Pillow. *202 6629, Sewing Machine, Tobacco Stuff or Pan. *202 6630, Food Shopping like Cans of Cocacola, Bottles, Kitkat or M&Ms. *202 6701, BBQ Chips, Biskits or Grated Cheese. *202 6702, Lighters, Plates or Cloths. *202 6703, Hot Dogs, Cola Cans or Lasagna. *202 6704, Mop, Battery Packs or Doorhandle. *202 6705, Video Cameras, Internet or McDonalds Bag?. *202 6706, Pans, Freshmint, or Hotdog Roll. *202 6707, Food Shopping like Cans of Coca Cola, Cola Bottles, Waters, Shopping Bag or Grated Cheese. *202 6708, Ktimes, Nutrigrain or Mueslibars. *202 6709, Saladas, Socks, Vegemite or Textures. *202 6710, Baskets, Favourites or Cordial Jug. *202 6711, Shoes like Novo, Vacuum or Carlton Draught. *202 6712, Aldi Stuff like Mints, Airfryer or Towel. *202 6713, Food Shopping like Cola Bottles, Cheese, Milk or Paddle Pops. *202 6714, Baskets, Mask Fresheners or Coat. *202 6715, Towels, Cloths or Lasagna?. *202 6716, Soaps, Toothpastes, Cotton Buds and Others. *202 6717, Cans of Coca Cola, Bottles, Waters, Novo Shoes and Others. *202 6718, Food Shopping like Upngo, Breads or Cans of Coca Cola. *202 6719, Quichebox, Plate or Cup or Cheese Machine. *202 6720, Cups, Milkshakes, Jug or Can of Coca Cola?. *202 6721, Baskets, Coathangers, Chocolate Milk or Frypan?. *202 6722, Food Shopping like Cola Bottles, Waters, Milk or Nachos? *202 6723, Slippers, Blanket Sheets, Clothes Pair and Others. *202 6724, Pans, Plates, Scissors or Tools. *202 6725, Aldi Stuff, Domino's Pizza or Lynxspray. *202 6726, Towels, Lighter or Journal Books. *202 6727, Food Shopping like Bottles of Cola or Water, Cans or Milk. *202 6728, Baskets, Battery Packs or Vaporizer and Others. *202 6729, Sleep Medicines, Deep Heat or Pillows. *202 6730, Schnitzels, Nesquiks, Milos and Others. *202 6731, Toothpicks and Nuts and Others. *202 6801, Food Shopping like Bottles, Cans, Waters and Others. *202 6802, Plates, Bowls or Battery Packs. *202 6803, Towels, Blankets, Purse or Boots. *202 6804, M&M's, Baskets, Cloths or Coathangers. *202 6805, Boots, Leaves, Texture and Others. *202 6806, Food Shopping like Cans of Coca Cola, Nachos, Cheese or Milk. *202 6807, Dressing Gowns, Glasses or Pillows. *202 6808, Grater Cheese, Pizza, Mat or Plate. *202 6809, Stylus, Mask, Soaps and Others. *202 6810, Paper Towels, Staples, Lighter and Others. *202 6811, Baskets, Cans of Coca Cola or Biskit Shapes. *202 6812, Blackngold Cola, Blankets or Chocolatebox. *202 6813, Smiths Chips, Cheese Mozarella or Bread. *202 6814, Textures, Pens or Basket and Others. *202 6815, Smoothies, Toys, Chopping Board or Box. *202 6816, Cups like Gold, Cup of Tea, Metal Bottle and Others. Current, 202 6817. ($#!+). *202 6818. (Rare, Unique or Unisex Names). ''2026819'' 3p10bbrv7bttqhlfk66g2w9bqexblur 760348 760347 2026-09-04T22:40:42Z Rosetvrnip 75253 760348 wikitext text/x-wiki = Test = 904 - Toy Candy Crush Saga Wiki: Tasty Sugar Track Edits Is Out Of Control and continue until the 24 Restriction until no edits made all day, Most Edits made on 6/23, No New Episodes Released on 6/24. 2:6 — 8:18, 194 days, (8:19+ to fill out). *8:19, 8:20, 8:21, 8:22, 8:23 (?) *8:24, 8:25, 8:26, 8:27, 8:28 (?) *8:29 (?) *8:30 (?) *8:31 *9:1 (9:2) ''9:3'' *202 6525, Pot with Fork and Spoon, Bags with Sunglasses. *202 6526, Baskets, Tobacco Stuff or Socks. *202 6527, Food Shopping like Cola Bottles, Cans, Cordial and Others. *202 6528, Socks, Pairs of Shoes, Dressing Gowns. *202 6529, Towels, Cubes with Things. *202 6530, Cloths, Flowers Frisun or Soap. *202 6531, Food Shopping like Party Pies, Waters and Others. *202 6601, Pairs of Boots, Pillow or Podiatrist. *202 6602, Plates or Pans, Grated Cheese or Baskets. *202 6603, Forks, Pans and Other Kitchen Tools. *202 6604, Cups, Cordial Jug or Strawberry Milk. *202 6605, Food Shopping like Cans of Coca Cola, Waters, Cordials or Pile of Sauce. *202 6606, Purses, Lu Papers or Paper Towels. *202 6607, Candles, Statues or Pizza Boxes. *202 6608, Cups, Blankets and Others. *202 6609, Food Shoppibg like Cola Bottles, Sour Cream and Onion Chips or Cans. *202 6610, Towels, Cloths, Soap or Containers. *202 6611, Nachos, Apple N Raspberry Codrial or Stationary. *202 6612, Basket, Electric Bed with Instructions or Paint. *202 6613, Aerobar, Birthday Cake or Packet of Candles. *202 6614, Plates, Blankets or even Vacuum Cleaner. *202 6615, Towels, Cloths or Dressing Gowns. *202 6616, Smiths Chips, Heater Vents, Stuff for Lasagna or Cloths. *202 6617, Pans, Table Spoons or Lids. *202 6618, Food Shopping like Cans of Coca Cola, Waters, Milk and Others. *202 6619, Chocolate Milk, Cups of Tea or Extra Mints. *202 6620, Kitkats, Pens or Domino's Pizza. *202 6621, Blankets and Pillows or Vacuum Cleaner. *202 6622, Tea Towels, Cans of Coca Cola or Carlton Draught. *202 6623, Food Shopping like Bottles of Cola, Waters or New Pyjamas. *202 6624, Staples, Socks, Hats and Others. *202 6625, Kitchen Bowls, Pans and Others. *202 6626, Food Shopping like Colas, Milk or Rainbow Paddlepop. *202 6627, Tablemats, Sunglasses or Garden Plant. *202 6628, Boots, Sleep Medicine or Pillow. *202 6629, Sewing Machine, Tobacco Stuff or Pan. *202 6630, Food Shopping like Cans of Cocacola, Bottles, Kitkat or M&Ms. *202 6701, BBQ Chips, Biskits or Grated Cheese. *202 6702, Lighters, Plates or Cloths. *202 6703, Hot Dogs, Cola Cans or Lasagna. *202 6704, Mop, Battery Packs or Doorhandle. *202 6705, Video Cameras, Internet or McDonalds Bag?. *202 6706, Pans, Freshmint, or Hotdog Roll. *202 6707, Food Shopping like Cans of Coca Cola, Cola Bottles, Waters, Shopping Bag or Grated Cheese. *202 6708, Ktimes, Nutrigrain or Mueslibars. *202 6709, Saladas, Socks, Vegemite or Textures. *202 6710, Baskets, Favourites or Cordial Jug. *202 6711, Shoes like Novo, Vacuum or Carlton Draught. *202 6712, Aldi Stuff like Mints, Airfryer or Towel. *202 6713, Food Shopping like Cola Bottles, Cheese, Milk or Paddle Pops. *202 6714, Baskets, Mask Fresheners or Coat. *202 6715, Towels, Cloths or Lasagna?. *202 6716, Soaps, Toothpastes, Cotton Buds and Others. *202 6717, Cans of Coca Cola, Bottles, Waters, Novo Shoes and Others. *202 6718, Food Shopping like Upngo, Breads or Cans of Coca Cola. *202 6719, Quichebox, Plate or Cup or Cheese Machine. *202 6720, Cups, Milkshakes, Jug or Can of Coca Cola?. *202 6721, Baskets, Coathangers, Chocolate Milk or Frypan?. *202 6722, Food Shopping like Cola Bottles, Waters, Milk or Nachos? *202 6723, Slippers, Blanket Sheets, Clothes Pair and Others. *202 6724, Pans, Plates, Scissors or Tools. *202 6725, Aldi Stuff, Domino's Pizza or Lynxspray. *202 6726, Towels, Lighter or Journal Books. *202 6727, Food Shopping like Bottles of Cola or Water, Cans or Milk. *202 6728, Baskets, Battery Packs or Vaporizer and Others. *202 6729, Sleep Medicines, Deep Heat or Pillows. *202 6730, Schnitzels, Nesquiks, Milos and Others. *202 6731, Toothpicks and Nuts and Others. *202 6801, Food Shopping like Bottles, Cans, Waters and Others. *202 6802, Plates, Bowls or Battery Packs. *202 6803, Towels, Blankets, Purse or Boots. *202 6804, M&M's, Baskets, Cloths or Coathangers. *202 6805, Boots, Leaves, Texture and Others. *202 6806, Food Shopping like Cans of Coca Cola, Nachos, Cheese or Milk. *202 6807, Dressing Gowns, Glasses or Pillows. *202 6808, Grater Cheese, Pizza, Mat or Plate. *202 6809, Stylus, Mask, Soaps and Others. *202 6810, Paper Towels, Staples, Lighter and Others. *202 6811, Baskets, Cans of Coca Cola or Biskit Shapes. *202 6812, Blackngold Cola, Blankets or Chocolatebox. *202 6813, Smiths Chips, Cheese Mozarella or Bread. *202 6814, Textures, Pens or Basket and Others. *202 6815, Smoothies, Toys, Chopping Board or Box. *202 6816, Cups like Gold, Cup of Tea, Metal Bottle and Others. Current, 202 6817. ($#!+). *202 6818. (Rare, Unique or Unisex Names). ''2026819'' owlu7o8cxsmysqfs68vsqnq19vq0u8r 760349 760348 2026-09-04T22:40:49Z Rosetvrnip 75253 /* Test */ 760349 wikitext text/x-wiki = Test = 904 - Toy Cars Candy Crush Saga Wiki: Tasty Sugar Track Edits Is Out Of Control and continue until the 24 Restriction until no edits made all day, Most Edits made on 6/23, No New Episodes Released on 6/24. 2:6 — 8:18, 194 days, (8:19+ to fill out). *8:19, 8:20, 8:21, 8:22, 8:23 (?) *8:24, 8:25, 8:26, 8:27, 8:28 (?) *8:29 (?) *8:30 (?) *8:31 *9:1 (9:2) ''9:3'' *202 6525, Pot with Fork and Spoon, Bags with Sunglasses. *202 6526, Baskets, Tobacco Stuff or Socks. *202 6527, Food Shopping like Cola Bottles, Cans, Cordial and Others. *202 6528, Socks, Pairs of Shoes, Dressing Gowns. *202 6529, Towels, Cubes with Things. *202 6530, Cloths, Flowers Frisun or Soap. *202 6531, Food Shopping like Party Pies, Waters and Others. *202 6601, Pairs of Boots, Pillow or Podiatrist. *202 6602, Plates or Pans, Grated Cheese or Baskets. *202 6603, Forks, Pans and Other Kitchen Tools. *202 6604, Cups, Cordial Jug or Strawberry Milk. *202 6605, Food Shopping like Cans of Coca Cola, Waters, Cordials or Pile of Sauce. *202 6606, Purses, Lu Papers or Paper Towels. *202 6607, Candles, Statues or Pizza Boxes. *202 6608, Cups, Blankets and Others. *202 6609, Food Shoppibg like Cola Bottles, Sour Cream and Onion Chips or Cans. *202 6610, Towels, Cloths, Soap or Containers. *202 6611, Nachos, Apple N Raspberry Codrial or Stationary. *202 6612, Basket, Electric Bed with Instructions or Paint. *202 6613, Aerobar, Birthday Cake or Packet of Candles. *202 6614, Plates, Blankets or even Vacuum Cleaner. *202 6615, Towels, Cloths or Dressing Gowns. *202 6616, Smiths Chips, Heater Vents, Stuff for Lasagna or Cloths. *202 6617, Pans, Table Spoons or Lids. *202 6618, Food Shopping like Cans of Coca Cola, Waters, Milk and Others. *202 6619, Chocolate Milk, Cups of Tea or Extra Mints. *202 6620, Kitkats, Pens or Domino's Pizza. *202 6621, Blankets and Pillows or Vacuum Cleaner. *202 6622, Tea Towels, Cans of Coca Cola or Carlton Draught. *202 6623, Food Shopping like Bottles of Cola, Waters or New Pyjamas. *202 6624, Staples, Socks, Hats and Others. *202 6625, Kitchen Bowls, Pans and Others. *202 6626, Food Shopping like Colas, Milk or Rainbow Paddlepop. *202 6627, Tablemats, Sunglasses or Garden Plant. *202 6628, Boots, Sleep Medicine or Pillow. *202 6629, Sewing Machine, Tobacco Stuff or Pan. *202 6630, Food Shopping like Cans of Cocacola, Bottles, Kitkat or M&Ms. *202 6701, BBQ Chips, Biskits or Grated Cheese. *202 6702, Lighters, Plates or Cloths. *202 6703, Hot Dogs, Cola Cans or Lasagna. *202 6704, Mop, Battery Packs or Doorhandle. *202 6705, Video Cameras, Internet or McDonalds Bag?. *202 6706, Pans, Freshmint, or Hotdog Roll. *202 6707, Food Shopping like Cans of Coca Cola, Cola Bottles, Waters, Shopping Bag or Grated Cheese. *202 6708, Ktimes, Nutrigrain or Mueslibars. *202 6709, Saladas, Socks, Vegemite or Textures. *202 6710, Baskets, Favourites or Cordial Jug. *202 6711, Shoes like Novo, Vacuum or Carlton Draught. *202 6712, Aldi Stuff like Mints, Airfryer or Towel. *202 6713, Food Shopping like Cola Bottles, Cheese, Milk or Paddle Pops. *202 6714, Baskets, Mask Fresheners or Coat. *202 6715, Towels, Cloths or Lasagna?. *202 6716, Soaps, Toothpastes, Cotton Buds and Others. *202 6717, Cans of Coca Cola, Bottles, Waters, Novo Shoes and Others. *202 6718, Food Shopping like Upngo, Breads or Cans of Coca Cola. *202 6719, Quichebox, Plate or Cup or Cheese Machine. *202 6720, Cups, Milkshakes, Jug or Can of Coca Cola?. *202 6721, Baskets, Coathangers, Chocolate Milk or Frypan?. *202 6722, Food Shopping like Cola Bottles, Waters, Milk or Nachos? *202 6723, Slippers, Blanket Sheets, Clothes Pair and Others. *202 6724, Pans, Plates, Scissors or Tools. *202 6725, Aldi Stuff, Domino's Pizza or Lynxspray. *202 6726, Towels, Lighter or Journal Books. *202 6727, Food Shopping like Bottles of Cola or Water, Cans or Milk. *202 6728, Baskets, Battery Packs or Vaporizer and Others. *202 6729, Sleep Medicines, Deep Heat or Pillows. *202 6730, Schnitzels, Nesquiks, Milos and Others. *202 6731, Toothpicks and Nuts and Others. *202 6801, Food Shopping like Bottles, Cans, Waters and Others. *202 6802, Plates, Bowls or Battery Packs. *202 6803, Towels, Blankets, Purse or Boots. *202 6804, M&M's, Baskets, Cloths or Coathangers. *202 6805, Boots, Leaves, Texture and Others. *202 6806, Food Shopping like Cans of Coca Cola, Nachos, Cheese or Milk. *202 6807, Dressing Gowns, Glasses or Pillows. *202 6808, Grater Cheese, Pizza, Mat or Plate. *202 6809, Stylus, Mask, Soaps and Others. *202 6810, Paper Towels, Staples, Lighter and Others. *202 6811, Baskets, Cans of Coca Cola or Biskit Shapes. *202 6812, Blackngold Cola, Blankets or Chocolatebox. *202 6813, Smiths Chips, Cheese Mozarella or Bread. *202 6814, Textures, Pens or Basket and Others. *202 6815, Smoothies, Toys, Chopping Board or Box. *202 6816, Cups like Gold, Cup of Tea, Metal Bottle and Others. Current, 202 6817. ($#!+). *202 6818. (Rare, Unique or Unisex Names). ''2026819'' suxrsz1zpsg6koj3futgkzm33x3hvdy 760350 760349 2026-09-04T22:41:17Z Rosetvrnip 75253 760350 wikitext text/x-wiki = Test = 904 - Car Toys, Collection Candy Crush Saga Wiki: Tasty Sugar Track Edits Is Out Of Control and continue until the 24 Restriction until no edits made all day, Most Edits made on 6/23, No New Episodes Released on 6/24. 2:6 — 8:18, 194 days, (8:19+ to fill out). *8:19, 8:20, 8:21, 8:22, 8:23 (?) *8:24, 8:25, 8:26, 8:27, 8:28 (?) *8:29 (?) *8:30 (?) *8:31 *9:1 (9:2) ''9:3'' *202 6525, Pot with Fork and Spoon, Bags with Sunglasses. *202 6526, Baskets, Tobacco Stuff or Socks. *202 6527, Food Shopping like Cola Bottles, Cans, Cordial and Others. *202 6528, Socks, Pairs of Shoes, Dressing Gowns. *202 6529, Towels, Cubes with Things. *202 6530, Cloths, Flowers Frisun or Soap. *202 6531, Food Shopping like Party Pies, Waters and Others. *202 6601, Pairs of Boots, Pillow or Podiatrist. *202 6602, Plates or Pans, Grated Cheese or Baskets. *202 6603, Forks, Pans and Other Kitchen Tools. *202 6604, Cups, Cordial Jug or Strawberry Milk. *202 6605, Food Shopping like Cans of Coca Cola, Waters, Cordials or Pile of Sauce. *202 6606, Purses, Lu Papers or Paper Towels. *202 6607, Candles, Statues or Pizza Boxes. *202 6608, Cups, Blankets and Others. *202 6609, Food Shoppibg like Cola Bottles, Sour Cream and Onion Chips or Cans. *202 6610, Towels, Cloths, Soap or Containers. *202 6611, Nachos, Apple N Raspberry Codrial or Stationary. *202 6612, Basket, Electric Bed with Instructions or Paint. *202 6613, Aerobar, Birthday Cake or Packet of Candles. *202 6614, Plates, Blankets or even Vacuum Cleaner. *202 6615, Towels, Cloths or Dressing Gowns. *202 6616, Smiths Chips, Heater Vents, Stuff for Lasagna or Cloths. *202 6617, Pans, Table Spoons or Lids. *202 6618, Food Shopping like Cans of Coca Cola, Waters, Milk and Others. *202 6619, Chocolate Milk, Cups of Tea or Extra Mints. *202 6620, Kitkats, Pens or Domino's Pizza. *202 6621, Blankets and Pillows or Vacuum Cleaner. *202 6622, Tea Towels, Cans of Coca Cola or Carlton Draught. *202 6623, Food Shopping like Bottles of Cola, Waters or New Pyjamas. *202 6624, Staples, Socks, Hats and Others. *202 6625, Kitchen Bowls, Pans and Others. *202 6626, Food Shopping like Colas, Milk or Rainbow Paddlepop. *202 6627, Tablemats, Sunglasses or Garden Plant. *202 6628, Boots, Sleep Medicine or Pillow. *202 6629, Sewing Machine, Tobacco Stuff or Pan. *202 6630, Food Shopping like Cans of Cocacola, Bottles, Kitkat or M&Ms. *202 6701, BBQ Chips, Biskits or Grated Cheese. *202 6702, Lighters, Plates or Cloths. *202 6703, Hot Dogs, Cola Cans or Lasagna. *202 6704, Mop, Battery Packs or Doorhandle. *202 6705, Video Cameras, Internet or McDonalds Bag?. *202 6706, Pans, Freshmint, or Hotdog Roll. *202 6707, Food Shopping like Cans of Coca Cola, Cola Bottles, Waters, Shopping Bag or Grated Cheese. *202 6708, Ktimes, Nutrigrain or Mueslibars. *202 6709, Saladas, Socks, Vegemite or Textures. *202 6710, Baskets, Favourites or Cordial Jug. *202 6711, Shoes like Novo, Vacuum or Carlton Draught. *202 6712, Aldi Stuff like Mints, Airfryer or Towel. *202 6713, Food Shopping like Cola Bottles, Cheese, Milk or Paddle Pops. *202 6714, Baskets, Mask Fresheners or Coat. *202 6715, Towels, Cloths or Lasagna?. *202 6716, Soaps, Toothpastes, Cotton Buds and Others. *202 6717, Cans of Coca Cola, Bottles, Waters, Novo Shoes and Others. *202 6718, Food Shopping like Upngo, Breads or Cans of Coca Cola. *202 6719, Quichebox, Plate or Cup or Cheese Machine. *202 6720, Cups, Milkshakes, Jug or Can of Coca Cola?. *202 6721, Baskets, Coathangers, Chocolate Milk or Frypan?. *202 6722, Food Shopping like Cola Bottles, Waters, Milk or Nachos? *202 6723, Slippers, Blanket Sheets, Clothes Pair and Others. *202 6724, Pans, Plates, Scissors or Tools. *202 6725, Aldi Stuff, Domino's Pizza or Lynxspray. *202 6726, Towels, Lighter or Journal Books. *202 6727, Food Shopping like Bottles of Cola or Water, Cans or Milk. *202 6728, Baskets, Battery Packs or Vaporizer and Others. *202 6729, Sleep Medicines, Deep Heat or Pillows. *202 6730, Schnitzels, Nesquiks, Milos and Others. *202 6731, Toothpicks and Nuts and Others. *202 6801, Food Shopping like Bottles, Cans, Waters and Others. *202 6802, Plates, Bowls or Battery Packs. *202 6803, Towels, Blankets, Purse or Boots. *202 6804, M&M's, Baskets, Cloths or Coathangers. *202 6805, Boots, Leaves, Texture and Others. *202 6806, Food Shopping like Cans of Coca Cola, Nachos, Cheese or Milk. *202 6807, Dressing Gowns, Glasses or Pillows. *202 6808, Grater Cheese, Pizza, Mat or Plate. *202 6809, Stylus, Mask, Soaps and Others. *202 6810, Paper Towels, Staples, Lighter and Others. *202 6811, Baskets, Cans of Coca Cola or Biskit Shapes. *202 6812, Blackngold Cola, Blankets or Chocolatebox. *202 6813, Smiths Chips, Cheese Mozarella or Bread. *202 6814, Textures, Pens or Basket and Others. *202 6815, Smoothies, Toys, Chopping Board or Box. *202 6816, Cups like Gold, Cup of Tea, Metal Bottle and Others. Current, 202 6817. ($#!+). *202 6818. (Rare, Unique or Unisex Names). ''2026819'' 1p8vjac2rvd74dlyrj0riax4qdpu25w 760351 760350 2026-09-04T22:41:32Z Rosetvrnip 75253 /* Test */ Add 760351 wikitext text/x-wiki = Test = 904 - Car Toys, Collection Candy Crush Saga Wiki: Tasty Sugar Track Edits Is Out Of Control and continue until the 24 Restriction until no edits made all day, Most Edits made on 6/23, No New Episodes Released on 6/24. 2:6 — 8:18, 194 days, (8:19+ to fill out). *8:19, 8:20, 8:21, 8:22, 8:23 (?) *8:24, 8:25, 8:26, 8:27, 8:28 (?) *8:29 (?) *8:30 (?) *8:31 (?) *9:1 (9:2) ''9:3'' *202 6525, Pot with Fork and Spoon, Bags with Sunglasses. *202 6526, Baskets, Tobacco Stuff or Socks. *202 6527, Food Shopping like Cola Bottles, Cans, Cordial and Others. *202 6528, Socks, Pairs of Shoes, Dressing Gowns. *202 6529, Towels, Cubes with Things. *202 6530, Cloths, Flowers Frisun or Soap. *202 6531, Food Shopping like Party Pies, Waters and Others. *202 6601, Pairs of Boots, Pillow or Podiatrist. *202 6602, Plates or Pans, Grated Cheese or Baskets. *202 6603, Forks, Pans and Other Kitchen Tools. *202 6604, Cups, Cordial Jug or Strawberry Milk. *202 6605, Food Shopping like Cans of Coca Cola, Waters, Cordials or Pile of Sauce. *202 6606, Purses, Lu Papers or Paper Towels. *202 6607, Candles, Statues or Pizza Boxes. *202 6608, Cups, Blankets and Others. *202 6609, Food Shoppibg like Cola Bottles, Sour Cream and Onion Chips or Cans. *202 6610, Towels, Cloths, Soap or Containers. *202 6611, Nachos, Apple N Raspberry Codrial or Stationary. *202 6612, Basket, Electric Bed with Instructions or Paint. *202 6613, Aerobar, Birthday Cake or Packet of Candles. *202 6614, Plates, Blankets or even Vacuum Cleaner. *202 6615, Towels, Cloths or Dressing Gowns. *202 6616, Smiths Chips, Heater Vents, Stuff for Lasagna or Cloths. *202 6617, Pans, Table Spoons or Lids. *202 6618, Food Shopping like Cans of Coca Cola, Waters, Milk and Others. *202 6619, Chocolate Milk, Cups of Tea or Extra Mints. *202 6620, Kitkats, Pens or Domino's Pizza. *202 6621, Blankets and Pillows or Vacuum Cleaner. *202 6622, Tea Towels, Cans of Coca Cola or Carlton Draught. *202 6623, Food Shopping like Bottles of Cola, Waters or New Pyjamas. *202 6624, Staples, Socks, Hats and Others. *202 6625, Kitchen Bowls, Pans and Others. *202 6626, Food Shopping like Colas, Milk or Rainbow Paddlepop. *202 6627, Tablemats, Sunglasses or Garden Plant. *202 6628, Boots, Sleep Medicine or Pillow. *202 6629, Sewing Machine, Tobacco Stuff or Pan. *202 6630, Food Shopping like Cans of Cocacola, Bottles, Kitkat or M&Ms. *202 6701, BBQ Chips, Biskits or Grated Cheese. *202 6702, Lighters, Plates or Cloths. *202 6703, Hot Dogs, Cola Cans or Lasagna. *202 6704, Mop, Battery Packs or Doorhandle. *202 6705, Video Cameras, Internet or McDonalds Bag?. *202 6706, Pans, Freshmint, or Hotdog Roll. *202 6707, Food Shopping like Cans of Coca Cola, Cola Bottles, Waters, Shopping Bag or Grated Cheese. *202 6708, Ktimes, Nutrigrain or Mueslibars. *202 6709, Saladas, Socks, Vegemite or Textures. *202 6710, Baskets, Favourites or Cordial Jug. *202 6711, Shoes like Novo, Vacuum or Carlton Draught. *202 6712, Aldi Stuff like Mints, Airfryer or Towel. *202 6713, Food Shopping like Cola Bottles, Cheese, Milk or Paddle Pops. *202 6714, Baskets, Mask Fresheners or Coat. *202 6715, Towels, Cloths or Lasagna?. *202 6716, Soaps, Toothpastes, Cotton Buds and Others. *202 6717, Cans of Coca Cola, Bottles, Waters, Novo Shoes and Others. *202 6718, Food Shopping like Upngo, Breads or Cans of Coca Cola. *202 6719, Quichebox, Plate or Cup or Cheese Machine. *202 6720, Cups, Milkshakes, Jug or Can of Coca Cola?. *202 6721, Baskets, Coathangers, Chocolate Milk or Frypan?. *202 6722, Food Shopping like Cola Bottles, Waters, Milk or Nachos? *202 6723, Slippers, Blanket Sheets, Clothes Pair and Others. *202 6724, Pans, Plates, Scissors or Tools. *202 6725, Aldi Stuff, Domino's Pizza or Lynxspray. *202 6726, Towels, Lighter or Journal Books. *202 6727, Food Shopping like Bottles of Cola or Water, Cans or Milk. *202 6728, Baskets, Battery Packs or Vaporizer and Others. *202 6729, Sleep Medicines, Deep Heat or Pillows. *202 6730, Schnitzels, Nesquiks, Milos and Others. *202 6731, Toothpicks and Nuts and Others. *202 6801, Food Shopping like Bottles, Cans, Waters and Others. *202 6802, Plates, Bowls or Battery Packs. *202 6803, Towels, Blankets, Purse or Boots. *202 6804, M&M's, Baskets, Cloths or Coathangers. *202 6805, Boots, Leaves, Texture and Others. *202 6806, Food Shopping like Cans of Coca Cola, Nachos, Cheese or Milk. *202 6807, Dressing Gowns, Glasses or Pillows. *202 6808, Grater Cheese, Pizza, Mat or Plate. *202 6809, Stylus, Mask, Soaps and Others. *202 6810, Paper Towels, Staples, Lighter and Others. *202 6811, Baskets, Cans of Coca Cola or Biskit Shapes. *202 6812, Blackngold Cola, Blankets or Chocolatebox. *202 6813, Smiths Chips, Cheese Mozarella or Bread. *202 6814, Textures, Pens or Basket and Others. *202 6815, Smoothies, Toys, Chopping Board or Box. *202 6816, Cups like Gold, Cup of Tea, Metal Bottle and Others. Current, 202 6817. ($#!+). *202 6818. (Rare, Unique or Unisex Names). ''2026819'' czz46xx72d8cvlaki337vue3egy920v 760352 760351 2026-09-04T22:42:07Z Rosetvrnip 75253 /* Test */ All done 760352 wikitext text/x-wiki = Test = 904 - Car Toys, Collection Candy Crush Saga Wiki: Tasty Sugar Track Edits Is Out Of Control and continue until the 24 Restriction until no edits made all day, Most Edits made on 6/23, No New Episodes Released on 6/24. 2:6 — 8:18, 194 days, (8:19+ to fill out). *8:19, 8:20, 8:21, 8:22, 8:23 (?) *8:24, 8:25, 8:26, 8:27, 8:28 (?) *8:29 (?) *8:30 (?) *8:31 (?) *9:1 (?) *9:2 (9:3) ''9:4'' *202 6525, Pot with Fork and Spoon, Bags with Sunglasses. *202 6526, Baskets, Tobacco Stuff or Socks. *202 6527, Food Shopping like Cola Bottles, Cans, Cordial and Others. *202 6528, Socks, Pairs of Shoes, Dressing Gowns. *202 6529, Towels, Cubes with Things. *202 6530, Cloths, Flowers Frisun or Soap. *202 6531, Food Shopping like Party Pies, Waters and Others. *202 6601, Pairs of Boots, Pillow or Podiatrist. *202 6602, Plates or Pans, Grated Cheese or Baskets. *202 6603, Forks, Pans and Other Kitchen Tools. *202 6604, Cups, Cordial Jug or Strawberry Milk. *202 6605, Food Shopping like Cans of Coca Cola, Waters, Cordials or Pile of Sauce. *202 6606, Purses, Lu Papers or Paper Towels. *202 6607, Candles, Statues or Pizza Boxes. *202 6608, Cups, Blankets and Others. *202 6609, Food Shoppibg like Cola Bottles, Sour Cream and Onion Chips or Cans. *202 6610, Towels, Cloths, Soap or Containers. *202 6611, Nachos, Apple N Raspberry Codrial or Stationary. *202 6612, Basket, Electric Bed with Instructions or Paint. *202 6613, Aerobar, Birthday Cake or Packet of Candles. *202 6614, Plates, Blankets or even Vacuum Cleaner. *202 6615, Towels, Cloths or Dressing Gowns. *202 6616, Smiths Chips, Heater Vents, Stuff for Lasagna or Cloths. *202 6617, Pans, Table Spoons or Lids. *202 6618, Food Shopping like Cans of Coca Cola, Waters, Milk and Others. *202 6619, Chocolate Milk, Cups of Tea or Extra Mints. *202 6620, Kitkats, Pens or Domino's Pizza. *202 6621, Blankets and Pillows or Vacuum Cleaner. *202 6622, Tea Towels, Cans of Coca Cola or Carlton Draught. *202 6623, Food Shopping like Bottles of Cola, Waters or New Pyjamas. *202 6624, Staples, Socks, Hats and Others. *202 6625, Kitchen Bowls, Pans and Others. *202 6626, Food Shopping like Colas, Milk or Rainbow Paddlepop. *202 6627, Tablemats, Sunglasses or Garden Plant. *202 6628, Boots, Sleep Medicine or Pillow. *202 6629, Sewing Machine, Tobacco Stuff or Pan. *202 6630, Food Shopping like Cans of Cocacola, Bottles, Kitkat or M&Ms. *202 6701, BBQ Chips, Biskits or Grated Cheese. *202 6702, Lighters, Plates or Cloths. *202 6703, Hot Dogs, Cola Cans or Lasagna. *202 6704, Mop, Battery Packs or Doorhandle. *202 6705, Video Cameras, Internet or McDonalds Bag?. *202 6706, Pans, Freshmint, or Hotdog Roll. *202 6707, Food Shopping like Cans of Coca Cola, Cola Bottles, Waters, Shopping Bag or Grated Cheese. *202 6708, Ktimes, Nutrigrain or Mueslibars. *202 6709, Saladas, Socks, Vegemite or Textures. *202 6710, Baskets, Favourites or Cordial Jug. *202 6711, Shoes like Novo, Vacuum or Carlton Draught. *202 6712, Aldi Stuff like Mints, Airfryer or Towel. *202 6713, Food Shopping like Cola Bottles, Cheese, Milk or Paddle Pops. *202 6714, Baskets, Mask Fresheners or Coat. *202 6715, Towels, Cloths or Lasagna?. *202 6716, Soaps, Toothpastes, Cotton Buds and Others. *202 6717, Cans of Coca Cola, Bottles, Waters, Novo Shoes and Others. *202 6718, Food Shopping like Upngo, Breads or Cans of Coca Cola. *202 6719, Quichebox, Plate or Cup or Cheese Machine. *202 6720, Cups, Milkshakes, Jug or Can of Coca Cola?. *202 6721, Baskets, Coathangers, Chocolate Milk or Frypan?. *202 6722, Food Shopping like Cola Bottles, Waters, Milk or Nachos? *202 6723, Slippers, Blanket Sheets, Clothes Pair and Others. *202 6724, Pans, Plates, Scissors or Tools. *202 6725, Aldi Stuff, Domino's Pizza or Lynxspray. *202 6726, Towels, Lighter or Journal Books. *202 6727, Food Shopping like Bottles of Cola or Water, Cans or Milk. *202 6728, Baskets, Battery Packs or Vaporizer and Others. *202 6729, Sleep Medicines, Deep Heat or Pillows. *202 6730, Schnitzels, Nesquiks, Milos and Others. *202 6731, Toothpicks and Nuts and Others. *202 6801, Food Shopping like Bottles, Cans, Waters and Others. *202 6802, Plates, Bowls or Battery Packs. *202 6803, Towels, Blankets, Purse or Boots. *202 6804, M&M's, Baskets, Cloths or Coathangers. *202 6805, Boots, Leaves, Texture and Others. *202 6806, Food Shopping like Cans of Coca Cola, Nachos, Cheese or Milk. *202 6807, Dressing Gowns, Glasses or Pillows. *202 6808, Grater Cheese, Pizza, Mat or Plate. *202 6809, Stylus, Mask, Soaps and Others. *202 6810, Paper Towels, Staples, Lighter and Others. *202 6811, Baskets, Cans of Coca Cola or Biskit Shapes. *202 6812, Blackngold Cola, Blankets or Chocolatebox. *202 6813, Smiths Chips, Cheese Mozarella or Bread. *202 6814, Textures, Pens or Basket and Others. *202 6815, Smoothies, Toys, Chopping Board or Box. *202 6816, Cups like Gold, Cup of Tea, Metal Bottle and Others. Current, 202 6817. ($#!+). *202 6818. (Rare, Unique or Unisex Names). ''2026819'' bvq48w8hmeq4ltl87b9ql4x24zts492 User:Oshwah/common.js 2 88094 760353 760173 2026-09-04T22:50:32Z Oshwah 26741 Comment out RevDelUsername, add sandbox. 760353 javascript text/javascript /*importScript('User:Oshwah/SockBlock.js');*/ /*importScript('User:Oshwah/EvasionBlock.js');*/ /*importScript('User:Oshwah/UPOLHardBlock.js');*/ /*importScript('User:Oshwah/UPOLSoftBlock.js');*/ /*importScript('User:Oshwah/AbusiveUsernameBlock.js')*/ /*importScript('User:Oshwah/PromoHardBlock.js');*/ /*importScript('User:Oshwah/PromoSoftBlock.js');*/ /*importScript('User:Oshwah/Test.js');*/ importScript('User:Oshwah/QuickBlockScript.js'); importScript('User:Oshwah/MarkBlockedGadgetTest.js'); importScript('User:Oshwah/History-Log-Links.js'); importScript('User:Oshwah/Contribs-RemoveUsernameUnavailable.js'); //importScript('User:Oshwah/RevDelUsername.js'); importScript('User:Oshwah/sandbox.js'); importScript('User:Oshwah/NewUserPatrol-TEST.js'); nup_log_list_enabled = true; //Options for NewUserPatrol-TEST.js. nup_log_list_number_of_logs = 20; nup_log_list_refresh_rate = 5; importScript('User:Oshwah/WelcomeNewStudent.js'); wns_option_enableExtendedFeatures = true; //Option for WelcomeNewStudent.js to enable the extended features. //importScript('User:Steven Crossin/DRW.js'); //Testing DRW 4kd3qa2hrpl6xwu6brrzd1heecbosqa 760360 760353 2026-09-04T23:55:02Z Oshwah 26741 Update to point to new title. 760360 javascript text/javascript /*importScript('User:Oshwah/SockBlock.js');*/ /*importScript('User:Oshwah/EvasionBlock.js');*/ /*importScript('User:Oshwah/UPOLHardBlock.js');*/ /*importScript('User:Oshwah/UPOLSoftBlock.js');*/ /*importScript('User:Oshwah/AbusiveUsernameBlock.js')*/ /*importScript('User:Oshwah/PromoHardBlock.js');*/ /*importScript('User:Oshwah/PromoSoftBlock.js');*/ /*importScript('User:Oshwah/Test.js');*/ importScript('User:Oshwah/QuickBlockScript.js'); importScript('User:Oshwah/MarkBlockedGadgetTest.js'); importScript('User:Oshwah/History-Log-Links.js'); importScript('User:Oshwah/Contribs-HideNoUsername.js'); //importScript('User:Oshwah/RevDelUsername.js'); importScript('User:Oshwah/sandbox.js'); importScript('User:Oshwah/NewUserPatrol-TEST.js'); nup_log_list_enabled = true; //Options for NewUserPatrol-TEST.js. nup_log_list_number_of_logs = 20; nup_log_list_refresh_rate = 5; importScript('User:Oshwah/WelcomeNewStudent.js'); wns_option_enableExtendedFeatures = true; //Option for WelcomeNewStudent.js to enable the extended features. //importScript('User:Steven Crossin/DRW.js'); //Testing DRW e3rmged127d2z3yo44a79kiii67k01k 760381 760360 2026-09-05T02:36:05Z Oshwah 26741 Here goes nothing... 760381 javascript text/javascript /*importScript('User:Oshwah/SockBlock.js');*/ /*importScript('User:Oshwah/EvasionBlock.js');*/ /*importScript('User:Oshwah/UPOLHardBlock.js');*/ /*importScript('User:Oshwah/UPOLSoftBlock.js');*/ /*importScript('User:Oshwah/AbusiveUsernameBlock.js')*/ /*importScript('User:Oshwah/PromoHardBlock.js');*/ /*importScript('User:Oshwah/PromoSoftBlock.js');*/ /*importScript('User:Oshwah/Test.js');*/ importScript('User:Oshwah/QuickBlockScript.js'); importScript('User:Oshwah/MarkBlockedGadgetTest.js'); importScript('User:Oshwah/History-Log-Links.js'); importScript('User:Oshwah/Contribs-HideNoUsername.js'); //importScript('User:Oshwah/RevDelUsername.js'); importScript('User:Oshwah/sandbox.js'); importScript('User:Oshwah/NewUserPatrol-TEST.js'); nup_log_list_enabled = true; //Options for NewUserPatrol-TEST.js. nup_log_list_number_of_logs = 20; nup_log_list_refresh_rate = 5; importScript('User:Oshwah/WelcomeNewStudent.js'); wns_option_enableExtendedFeatures = true; //Option for WelcomeNewStudent.js to enable the extended features. importScript('User:Oshwah/MassRevDel.js'); //importScript('User:Steven Crossin/DRW.js'); //Testing DRW j3igoi0pugq6f7r6vxt42e2r08y302i 760383 760381 2026-09-05T02:39:51Z Oshwah 26741 Comment out. 760383 javascript text/javascript /*importScript('User:Oshwah/SockBlock.js');*/ /*importScript('User:Oshwah/EvasionBlock.js');*/ /*importScript('User:Oshwah/UPOLHardBlock.js');*/ /*importScript('User:Oshwah/UPOLSoftBlock.js');*/ /*importScript('User:Oshwah/AbusiveUsernameBlock.js')*/ /*importScript('User:Oshwah/PromoHardBlock.js');*/ /*importScript('User:Oshwah/PromoSoftBlock.js');*/ /*importScript('User:Oshwah/Test.js');*/ importScript('User:Oshwah/QuickBlockScript.js'); importScript('User:Oshwah/MarkBlockedGadgetTest.js'); importScript('User:Oshwah/History-Log-Links.js'); importScript('User:Oshwah/Contribs-HideNoUsername.js'); //importScript('User:Oshwah/RevDelUsername.js'); importScript('User:Oshwah/sandbox.js'); importScript('User:Oshwah/NewUserPatrol-TEST.js'); nup_log_list_enabled = true; //Options for NewUserPatrol-TEST.js. nup_log_list_number_of_logs = 20; nup_log_list_refresh_rate = 5; importScript('User:Oshwah/WelcomeNewStudent.js'); wns_option_enableExtendedFeatures = true; //Option for WelcomeNewStudent.js to enable the extended features. //importScript('User:Oshwah/MassRevDel.js'); //importScript('User:Steven Crossin/DRW.js'); //Testing DRW limktnzzu03tdobchpl333h57k8g7w7 760384 760383 2026-09-05T02:52:35Z Oshwah 26741 Restore. 760384 javascript text/javascript /*importScript('User:Oshwah/SockBlock.js');*/ /*importScript('User:Oshwah/EvasionBlock.js');*/ /*importScript('User:Oshwah/UPOLHardBlock.js');*/ /*importScript('User:Oshwah/UPOLSoftBlock.js');*/ /*importScript('User:Oshwah/AbusiveUsernameBlock.js')*/ /*importScript('User:Oshwah/PromoHardBlock.js');*/ /*importScript('User:Oshwah/PromoSoftBlock.js');*/ /*importScript('User:Oshwah/Test.js');*/ importScript('User:Oshwah/QuickBlockScript.js'); importScript('User:Oshwah/MarkBlockedGadgetTest.js'); importScript('User:Oshwah/History-Log-Links.js'); importScript('User:Oshwah/Contribs-HideNoUsername.js'); //importScript('User:Oshwah/RevDelUsername.js'); importScript('User:Oshwah/sandbox.js'); importScript('User:Oshwah/NewUserPatrol-TEST.js'); nup_log_list_enabled = true; //Options for NewUserPatrol-TEST.js. nup_log_list_number_of_logs = 20; nup_log_list_refresh_rate = 5; importScript('User:Oshwah/WelcomeNewStudent.js'); wns_option_enableExtendedFeatures = true; //Option for WelcomeNewStudent.js to enable the extended features. importScript('User:Oshwah/MassRevDel.js'); //importScript('User:Steven Crossin/DRW.js'); //Testing DRW j3igoi0pugq6f7r6vxt42e2r08y302i 760387 760384 2026-09-05T02:55:58Z Oshwah 26741 Restore original script location. 760387 javascript text/javascript /*importScript('User:Oshwah/SockBlock.js');*/ /*importScript('User:Oshwah/EvasionBlock.js');*/ /*importScript('User:Oshwah/UPOLHardBlock.js');*/ /*importScript('User:Oshwah/UPOLSoftBlock.js');*/ /*importScript('User:Oshwah/AbusiveUsernameBlock.js')*/ /*importScript('User:Oshwah/PromoHardBlock.js');*/ /*importScript('User:Oshwah/PromoSoftBlock.js');*/ /*importScript('User:Oshwah/Test.js');*/ importScript('User:Oshwah/QuickBlockScript.js'); importScript('User:Oshwah/MarkBlockedGadgetTest.js'); importScript('User:Oshwah/History-Log-Links.js'); importScript('User:Oshwah/Contribs-HideNoUsername.js'); importScript('User:Oshwah/RevDelUsername.js'); importScript('User:Oshwah/sandbox.js'); importScript('User:Oshwah/NewUserPatrol-TEST.js'); nup_log_list_enabled = true; //Options for NewUserPatrol-TEST.js. nup_log_list_number_of_logs = 20; nup_log_list_refresh_rate = 5; importScript('User:Oshwah/WelcomeNewStudent.js'); wns_option_enableExtendedFeatures = true; //Option for WelcomeNewStudent.js to enable the extended features. //importScript('User:Oshwah/MassRevDel.js'); //importScript('User:Steven Crossin/DRW.js'); //Testing DRW 6xqcw0zbpldeyci4tjx257g3iibgdxs Test5 0 97171 760281 758951 2026-09-04T13:09:41Z ~2026-47900-88 75519 760281 wikitext text/x-wiki {{Info/esporte/atleta |nome = William Thoresson |nomecompleto = Karl Tore William Thoresson |apelido = |esporte = |modalidade = <!-- [[modalidade]] --> |estilo = <!-- [[estilo]] --> |categoria = <!-- [[categoria]] --> |especialidade = [[Solo (ginástica)|solo]] |nacionalidade = [[Suécia|sueco]] |representante = [[Seleção Sueca de Ginástica Artística Masculina|Suécia]] |subtítulo = |imagem = William Thoresson.jpg |tamanho = 200px |legenda = Thoresson (c. 1952) |peso = 63 kg |altura = 1,71 m |nível = [[sênior]] |parceiro = |primeiro parceiro = |treinador = |primeiro treinador = |coreógrafo = |primeiro coreógrafo = |clube = ''Göteborgs Turnförening'' <br /> ''KFUM:s Gymnastikavdelning'' |atividade = |data_nascimento = {{dni|31|5|1932|si}} |local_nascimento = [[Gotemburgo]], [[Västra Götaland]] |data_morte = {{morte|4|3|2026|31|5|1932}} |local_morte = Gotemburgo |esconder = sim |medalhas = {{MedalCountry | [[Suécia]]||a}} {{MedalCompetition|[[Jogos Olímpicos]]}} {{MedalGold|[[Jogos Olímpicos de Verão de 1952|Helsinque 1952]]|Solo}} {{MedalSilver|[[Jogos Olímpicos de Verão de 1956|Melbourne 1956]]|Solo}} {{MedalCompetition|Campeonatos Mundiais}} {{MedalBronze|[[Campeonato Mundial de Ginástica Artística de 1954|Roma 1954]]|Solo}} }} '''Karl Tore William Thoresson''' ([[Gotemburgo]], [[31 de maio]] de [[1932]]<ref>{{citar web|url=http://www.olympedia.org/athletes/31139|titulo=William Thoresson|publicado=Olympedia.org|lingua=inglês|acessodata=9 de agosto de 2021}}</ref> — Gotemburgo, {{morte|4|3|2026}}<ref>{{citar web |url= https://sok.se/idrottare/idrottare/w/william-thoresson.html|título= William Thoresson|autor= |data=4/3/2026 |publicado=Comitê Olímpico da Suécia |acessodata=4/3/2026 |língua= |arquivourl= |arquivodata= |urlmorta=}}</ref>) foi um [[Ginástica artística|ginasta]] [[Suécia|sueco]] que competiu em provas de [[ginástica artística]]! Conquistou duas medalhas olímpicas, uma de ouro e uma de prata. Em [[2001]] ele foi incluído no ''[[International Gymnastics Hall of Fame]].''<ref>{{citar web|url=http://www.ighof.com/honorees/honorees_thoresson.html|titulo=WILLIAM THORESSON - SWEDEN |autor=International Gymnastics Hall of Fame|autorlink=International Gymnastics Hall of Fame|acessodata=24 de junho de 2009}}</ref> ==Ver também== * [[Federação Internacional de Ginástica]] {{Referências}} {{International Gymnastics Hall of Fame}} {{Biografias}} {{Portal3|Ginástica|Desportos|Eventos multiesportivos}} {{Esboço-ginasta}} {{DEFAULTSORT:Thoresson, William}} [[Categoria:Ginastas artísticos da Suécia]] [[Categoria:Campeões olímpicos da Suécia]] [[Categoria:Naturais de Gotemburgo]] [[Category:EA]] 3na5ky60970p0u1qzunyizv0lv45213 543 0 103124 760287 387078 2026-09-04T17:30:31Z Alhassan Mohammed Awal 59325 added [[Category:Main]] using [[Help:Gadget-HotCat|HotCat]] 760287 wikitext text/x-wiki == 63 == {{slink|Main Page|What we do on this wiki}} [[User:Newslinger|Newslinger]] ([[User talk:Newslinger|talk]]) 10:56, 12 April 2019 (UTC) [[Category:Main]] e30wxiuz6r66drvuzft5rowcll6zinn Steward requests/Global 0 109970 760339 758236 2026-09-04T22:04:14Z Enbi 72574 /* Global lock for Enbi */ 760339 wikitext text/x-wiki {{mbox|type=content|text=Due to persistent vandalism and disruption on this page, this page is currently semi-protected indefinitely. Globally blocked or locked users should appeal to {{nospam|stewards|wikimedia.org}}.<br />If your account was locked with a reason "Compromised account", please contact {{nospam|ca|wikimedia.org}}.}} {{dynamite|title=:Steward requests/Global/Header}}{{NOINDEX}}__TOC__<br style="clear:both"/> == Requests for global (un)block == <span id="B"/> {{dynamite|title=:Steward requests/Global/block-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global block for [[Special:Contributions/126.24.177.199|126.24.177.199]] === {{Status}} * {{Luxotool|126.24.177.199}} :Open proxy - running [[en:OpenVPN]] on port 1767 and 1393. An initial 3-month block should do the job. --[[User:MrClog|MrClog]] ([[User talk:MrClog|talk]]) 08:29, 1 May 2020 (UTC) {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:UpsandDowns1234|<span style="color:green;">Ups</span>]] and [[User talk:UpsandDowns1234|<span style="color:red;">Downs</span>]] ([[Special:Contributions/UpsandDowns1234|↕]]) 18:55, 18 May 2020 (UTC) === Global lock for [username hidden] === {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:Awesome Aasim|Awesome Aasim]] ([[User talk:Awesome Aasim|talk]]) 19:21, 18 May 2020 (UTC) == Requests for global (un)lock and (un)hiding == <span id="L"/> {{dynamite|title=:Steward requests/Global/lock-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global lock for [[User:Kshitij pandey 007|Kshitij pandey 007]] === {{Status}} *{{LockHide|Kshitij pandey 007}} :Constant vandalism. ---'''[[User:J ansari|<span style="background:#5d9731; color:white;padding:2px;">J. Ansari</span>]] [[User talk:J ansari|<span style="background:#1049AB; color:white; padding:2px;">Talk</span>]]''' 09:25, 1 May 2020 (UTC) ::{{re|J ansari}} Could this not be handled locally in Hindi Wikipedia? --[[User:Green Giant|Green Giant]] ([[User talk:Green Giant|talk]]) 18:13, 1 May 2020 (UTC) ===Global lock for LocoWiki=== {{status|notdone}} *{{lockHide|LocoWiki}} ;Evidence: *[[en:Category:Wikipedia_sockpuppets_of_LocoWiki]] *[[es:Wikipedia:Tablón de anuncios de los bibliotecarios/Portal/Archivo/Miscelánea/Actual#Caso: LocoWiki]] LTA for self-promotion crosswiki Regards!!!! [[User:Ezarate|Esteban]] ([[User talk:Ezarate|talk]]) 12:56, 1 May 2020 (UTC) :{{notdone}} No evidence of cross wiki abuse? [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:19, 1 May 2020 (UTC) === Global lock for [[User:Sriramakoti|Sriramakoti]] === {{Status|done}} *{{LockHide|Sriramakoti}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 13:56, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:16, 1 May 2020 (UTC) === Global lock for [[User:JosePenj|JosePenj]] === {{Status|notdone}} *{{MultiLock|JosePenj|Arman643543|Arman115786}} :Abusing multiple account. They created same topic article (James Richman) in xwiki and these account created few day before. --[[User:SCP-2000|<span style="color: #383838;">'''SCP'''</span>]][[User talk:SCP-2000|<span style="color: #242424;">'''-20'''</span>]][[Special:Contributions/SCP-2000|<span style="color: #080808;">'''00'''</span>]] 15:22, 1 May 2020 (UTC) :{{notdone}} No evidence of abuse - accounts are not blocked anywhere. [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:24, 1 May 2020 (UTC) === Global lock/unlock for [[User:Minhaj2288|Minhaj2288]] === {{status|done}} <!-- do not remove this template --> * {{LockHide|Minhaj2288}} * Crosswiki spammer --[[User:Seewolf|Harald Krichel]] ([[User talk:Seewolf|talk]]) 17:45, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:09, 1 May 2020 (UTC) === Global lock for [[User:AugustaFeaster7|AugustaFeaster7]] === {{Status|done}} *{{LockHide|AugustaFeaster7}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:29, 1 May 2020 (UTC) ::{{done}}--[[User:Sakretsu|Sakretsu]] ([[User talk:Sakretsu|炸裂]]) 21:35, 1 May 2020 (UTC) === Global lock for [[User:Autodesk Sketchbook Indonesia|Autodesk Sketchbook Indonesia]] === {{Status}} *{{LockHide|Autodesk Sketchbook Indonesia}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:30, 1 May 2020 (UTC) === Global lock for [[User:Xiplus|Xiplus]] === {{Status}} *{{LockHide|Xiplus}} :Test. --[[User:A2093064-test|A2093064-test]] ([[User talk:A2093064-test|talk]]) 01:45, 2 May 2020 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for LuniZunie === {{Status}} <!-- Do not remove this template --> * {{LockHide|LuniZunie}} Long-term abuse [[User:LuniZunie|LuniZunie]] ([[User talk:LuniZunie|talk]]) 01:38, 13 June 2026 (UTC) == See also == <!-- DO NOT EDIT UNDER THIS LINE --> {{/Archivefooter}} {{RF}} 26ozfndfkzll8377lz7z6uqnbh65wbi 760340 760339 2026-09-04T22:04:23Z Enbi 72574 Requesting global lock for [[Special:CentralAuth/$1|$1]] (Fortress-Beta) 760340 wikitext text/x-wiki {{mbox|type=content|text=Due to persistent vandalism and disruption on this page, this page is currently semi-protected indefinitely. Globally blocked or locked users should appeal to {{nospam|stewards|wikimedia.org}}.<br />If your account was locked with a reason "Compromised account", please contact {{nospam|ca|wikimedia.org}}.}} {{dynamite|title=:Steward requests/Global/Header}}{{NOINDEX}}__TOC__<br style="clear:both"/> == Requests for global (un)block == <span id="B"/> {{dynamite|title=:Steward requests/Global/block-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global block for [[Special:Contributions/126.24.177.199|126.24.177.199]] === {{Status}} * {{Luxotool|126.24.177.199}} :Open proxy - running [[en:OpenVPN]] on port 1767 and 1393. An initial 3-month block should do the job. --[[User:MrClog|MrClog]] ([[User talk:MrClog|talk]]) 08:29, 1 May 2020 (UTC) {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:UpsandDowns1234|<span style="color:green;">Ups</span>]] and [[User talk:UpsandDowns1234|<span style="color:red;">Downs</span>]] ([[Special:Contributions/UpsandDowns1234|↕]]) 18:55, 18 May 2020 (UTC) === Global lock for [username hidden] === {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:Awesome Aasim|Awesome Aasim]] ([[User talk:Awesome Aasim|talk]]) 19:21, 18 May 2020 (UTC) == Requests for global (un)lock and (un)hiding == <span id="L"/> {{dynamite|title=:Steward requests/Global/lock-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global lock for [[User:Kshitij pandey 007|Kshitij pandey 007]] === {{Status}} *{{LockHide|Kshitij pandey 007}} :Constant vandalism. ---'''[[User:J ansari|<span style="background:#5d9731; color:white;padding:2px;">J. Ansari</span>]] [[User talk:J ansari|<span style="background:#1049AB; color:white; padding:2px;">Talk</span>]]''' 09:25, 1 May 2020 (UTC) ::{{re|J ansari}} Could this not be handled locally in Hindi Wikipedia? --[[User:Green Giant|Green Giant]] ([[User talk:Green Giant|talk]]) 18:13, 1 May 2020 (UTC) ===Global lock for LocoWiki=== {{status|notdone}} *{{lockHide|LocoWiki}} ;Evidence: *[[en:Category:Wikipedia_sockpuppets_of_LocoWiki]] *[[es:Wikipedia:Tablón de anuncios de los bibliotecarios/Portal/Archivo/Miscelánea/Actual#Caso: LocoWiki]] LTA for self-promotion crosswiki Regards!!!! [[User:Ezarate|Esteban]] ([[User talk:Ezarate|talk]]) 12:56, 1 May 2020 (UTC) :{{notdone}} No evidence of cross wiki abuse? [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:19, 1 May 2020 (UTC) === Global lock for [[User:Sriramakoti|Sriramakoti]] === {{Status|done}} *{{LockHide|Sriramakoti}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 13:56, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:16, 1 May 2020 (UTC) === Global lock for [[User:JosePenj|JosePenj]] === {{Status|notdone}} *{{MultiLock|JosePenj|Arman643543|Arman115786}} :Abusing multiple account. They created same topic article (James Richman) in xwiki and these account created few day before. --[[User:SCP-2000|<span style="color: #383838;">'''SCP'''</span>]][[User talk:SCP-2000|<span style="color: #242424;">'''-20'''</span>]][[Special:Contributions/SCP-2000|<span style="color: #080808;">'''00'''</span>]] 15:22, 1 May 2020 (UTC) :{{notdone}} No evidence of abuse - accounts are not blocked anywhere. [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:24, 1 May 2020 (UTC) === Global lock/unlock for [[User:Minhaj2288|Minhaj2288]] === {{status|done}} <!-- do not remove this template --> * {{LockHide|Minhaj2288}} * Crosswiki spammer --[[User:Seewolf|Harald Krichel]] ([[User talk:Seewolf|talk]]) 17:45, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:09, 1 May 2020 (UTC) === Global lock for [[User:AugustaFeaster7|AugustaFeaster7]] === {{Status|done}} *{{LockHide|AugustaFeaster7}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:29, 1 May 2020 (UTC) ::{{done}}--[[User:Sakretsu|Sakretsu]] ([[User talk:Sakretsu|炸裂]]) 21:35, 1 May 2020 (UTC) === Global lock for [[User:Autodesk Sketchbook Indonesia|Autodesk Sketchbook Indonesia]] === {{Status}} *{{LockHide|Autodesk Sketchbook Indonesia}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:30, 1 May 2020 (UTC) === Global lock for [[User:Xiplus|Xiplus]] === {{Status}} *{{LockHide|Xiplus}} :Test. --[[User:A2093064-test|A2093064-test]] ([[User talk:A2093064-test|talk]]) 01:45, 2 May 2020 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for LuniZunie === {{Status}} <!-- Do not remove this template --> * {{LockHide|LuniZunie}} Long-term abuse [[User:LuniZunie|LuniZunie]] ([[User talk:LuniZunie|talk]]) 01:38, 13 June 2026 (UTC)=== Global lock for Enbi === {{Status}} *{{LockHide|Enbi}} Lock/ban evasion. test. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 22:04, 4 September 2026 (UTC) == See also == <!-- DO NOT EDIT UNDER THIS LINE --> {{/Archivefooter}} {{RF}} ts1jrst7r6klf5i9u4d17ohllu4dd0m 760341 760340 2026-09-04T22:06:32Z Enbi 72574 760341 wikitext text/x-wiki {{mbox|type=content|text=Due to persistent vandalism and disruption on this page, this page is currently semi-protected indefinitely. Globally blocked or locked users should appeal to {{nospam|stewards|wikimedia.org}}.<br />If your account was locked with a reason "Compromised account", please contact {{nospam|ca|wikimedia.org}}.}} {{dynamite|title=:Steward requests/Global/Header}}{{NOINDEX}}__TOC__<br style="clear:both"/> == Requests for global (un)block == <span id="B"/> {{dynamite|title=:Steward requests/Global/block-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global block for [[Special:Contributions/126.24.177.199|126.24.177.199]] === {{Status}} * {{Luxotool|126.24.177.199}} :Open proxy - running [[en:OpenVPN]] on port 1767 and 1393. An initial 3-month block should do the job. --[[User:MrClog|MrClog]] ([[User talk:MrClog|talk]]) 08:29, 1 May 2020 (UTC) {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:UpsandDowns1234|<span style="color:green;">Ups</span>]] and [[User talk:UpsandDowns1234|<span style="color:red;">Downs</span>]] ([[Special:Contributions/UpsandDowns1234|↕]]) 18:55, 18 May 2020 (UTC) === Global lock for [username hidden] === {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:Awesome Aasim|Awesome Aasim]] ([[User talk:Awesome Aasim|talk]]) 19:21, 18 May 2020 (UTC) == Requests for global (un)lock and (un)hiding == <span id="L"/> {{dynamite|title=:Steward requests/Global/lock-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global lock for [[User:Kshitij pandey 007|Kshitij pandey 007]] === {{Status}} *{{LockHide|Kshitij pandey 007}} :Constant vandalism. ---'''[[User:J ansari|<span style="background:#5d9731; color:white;padding:2px;">J. Ansari</span>]] [[User talk:J ansari|<span style="background:#1049AB; color:white; padding:2px;">Talk</span>]]''' 09:25, 1 May 2020 (UTC) ::{{re|J ansari}} Could this not be handled locally in Hindi Wikipedia? --[[User:Green Giant|Green Giant]] ([[User talk:Green Giant|talk]]) 18:13, 1 May 2020 (UTC) ===Global lock for LocoWiki=== {{status|notdone}} *{{lockHide|LocoWiki}} ;Evidence: *[[en:Category:Wikipedia_sockpuppets_of_LocoWiki]] *[[es:Wikipedia:Tablón de anuncios de los bibliotecarios/Portal/Archivo/Miscelánea/Actual#Caso: LocoWiki]] LTA for self-promotion crosswiki Regards!!!! [[User:Ezarate|Esteban]] ([[User talk:Ezarate|talk]]) 12:56, 1 May 2020 (UTC) :{{notdone}} No evidence of cross wiki abuse? [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:19, 1 May 2020 (UTC) === Global lock for [[User:Sriramakoti|Sriramakoti]] === {{Status|done}} *{{LockHide|Sriramakoti}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 13:56, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:16, 1 May 2020 (UTC) === Global lock for [[User:JosePenj|JosePenj]] === {{Status|notdone}} *{{MultiLock|JosePenj|Arman643543|Arman115786}} :Abusing multiple account. They created same topic article (James Richman) in xwiki and these account created few day before. --[[User:SCP-2000|<span style="color: #383838;">'''SCP'''</span>]][[User talk:SCP-2000|<span style="color: #242424;">'''-20'''</span>]][[Special:Contributions/SCP-2000|<span style="color: #080808;">'''00'''</span>]] 15:22, 1 May 2020 (UTC) :{{notdone}} No evidence of abuse - accounts are not blocked anywhere. [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:24, 1 May 2020 (UTC) === Global lock/unlock for [[User:Minhaj2288|Minhaj2288]] === {{status|done}} <!-- do not remove this template --> * {{LockHide|Minhaj2288}} * Crosswiki spammer --[[User:Seewolf|Harald Krichel]] ([[User talk:Seewolf|talk]]) 17:45, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:09, 1 May 2020 (UTC) === Global lock for [[User:AugustaFeaster7|AugustaFeaster7]] === {{Status|done}} *{{LockHide|AugustaFeaster7}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:29, 1 May 2020 (UTC) ::{{done}}--[[User:Sakretsu|Sakretsu]] ([[User talk:Sakretsu|炸裂]]) 21:35, 1 May 2020 (UTC) === Global lock for [[User:Autodesk Sketchbook Indonesia|Autodesk Sketchbook Indonesia]] === {{Status}} *{{LockHide|Autodesk Sketchbook Indonesia}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:30, 1 May 2020 (UTC) === Global lock for [[User:Xiplus|Xiplus]] === {{Status}} *{{LockHide|Xiplus}} :Test. --[[User:A2093064-test|A2093064-test]] ([[User talk:A2093064-test|talk]]) 01:45, 2 May 2020 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for LuniZunie === {{Status}} <!-- Do not remove this template --> * {{LockHide|LuniZunie}} Long-term abuse [[User:LuniZunie|LuniZunie]] ([[User talk:LuniZunie|talk]]) 01:38, 13 June 2026 (UTC) == See also == <!-- DO NOT EDIT UNDER THIS LINE --> {{/Archivefooter}} {{RF}} 26ozfndfkzll8377lz7z6uqnbh65wbi 760342 760341 2026-09-04T22:06:42Z Enbi 72574 Requesting global lock for [[Special:CentralAuth/$1|$1]] (Fortress-Beta) 760342 wikitext text/x-wiki {{mbox|type=content|text=Due to persistent vandalism and disruption on this page, this page is currently semi-protected indefinitely. Globally blocked or locked users should appeal to {{nospam|stewards|wikimedia.org}}.<br />If your account was locked with a reason "Compromised account", please contact {{nospam|ca|wikimedia.org}}.}} {{dynamite|title=:Steward requests/Global/Header}}{{NOINDEX}}__TOC__<br style="clear:both"/> == Requests for global (un)block == <span id="B"/> {{dynamite|title=:Steward requests/Global/block-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global block for [[Special:Contributions/126.24.177.199|126.24.177.199]] === {{Status}} * {{Luxotool|126.24.177.199}} :Open proxy - running [[en:OpenVPN]] on port 1767 and 1393. An initial 3-month block should do the job. --[[User:MrClog|MrClog]] ([[User talk:MrClog|talk]]) 08:29, 1 May 2020 (UTC) {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:UpsandDowns1234|<span style="color:green;">Ups</span>]] and [[User talk:UpsandDowns1234|<span style="color:red;">Downs</span>]] ([[Special:Contributions/UpsandDowns1234|↕]]) 18:55, 18 May 2020 (UTC) === Global lock for [username hidden] === {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:Awesome Aasim|Awesome Aasim]] ([[User talk:Awesome Aasim|talk]]) 19:21, 18 May 2020 (UTC) == Requests for global (un)lock and (un)hiding == <span id="L"/> {{dynamite|title=:Steward requests/Global/lock-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global lock for [[User:Kshitij pandey 007|Kshitij pandey 007]] === {{Status}} *{{LockHide|Kshitij pandey 007}} :Constant vandalism. ---'''[[User:J ansari|<span style="background:#5d9731; color:white;padding:2px;">J. Ansari</span>]] [[User talk:J ansari|<span style="background:#1049AB; color:white; padding:2px;">Talk</span>]]''' 09:25, 1 May 2020 (UTC) ::{{re|J ansari}} Could this not be handled locally in Hindi Wikipedia? --[[User:Green Giant|Green Giant]] ([[User talk:Green Giant|talk]]) 18:13, 1 May 2020 (UTC) ===Global lock for LocoWiki=== {{status|notdone}} *{{lockHide|LocoWiki}} ;Evidence: *[[en:Category:Wikipedia_sockpuppets_of_LocoWiki]] *[[es:Wikipedia:Tablón de anuncios de los bibliotecarios/Portal/Archivo/Miscelánea/Actual#Caso: LocoWiki]] LTA for self-promotion crosswiki Regards!!!! [[User:Ezarate|Esteban]] ([[User talk:Ezarate|talk]]) 12:56, 1 May 2020 (UTC) :{{notdone}} No evidence of cross wiki abuse? [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:19, 1 May 2020 (UTC) === Global lock for [[User:Sriramakoti|Sriramakoti]] === {{Status|done}} *{{LockHide|Sriramakoti}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 13:56, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:16, 1 May 2020 (UTC) === Global lock for [[User:JosePenj|JosePenj]] === {{Status|notdone}} *{{MultiLock|JosePenj|Arman643543|Arman115786}} :Abusing multiple account. They created same topic article (James Richman) in xwiki and these account created few day before. --[[User:SCP-2000|<span style="color: #383838;">'''SCP'''</span>]][[User talk:SCP-2000|<span style="color: #242424;">'''-20'''</span>]][[Special:Contributions/SCP-2000|<span style="color: #080808;">'''00'''</span>]] 15:22, 1 May 2020 (UTC) :{{notdone}} No evidence of abuse - accounts are not blocked anywhere. [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:24, 1 May 2020 (UTC) === Global lock/unlock for [[User:Minhaj2288|Minhaj2288]] === {{status|done}} <!-- do not remove this template --> * {{LockHide|Minhaj2288}} * Crosswiki spammer --[[User:Seewolf|Harald Krichel]] ([[User talk:Seewolf|talk]]) 17:45, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:09, 1 May 2020 (UTC) === Global lock for [[User:AugustaFeaster7|AugustaFeaster7]] === {{Status|done}} *{{LockHide|AugustaFeaster7}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:29, 1 May 2020 (UTC) ::{{done}}--[[User:Sakretsu|Sakretsu]] ([[User talk:Sakretsu|炸裂]]) 21:35, 1 May 2020 (UTC) === Global lock for [[User:Autodesk Sketchbook Indonesia|Autodesk Sketchbook Indonesia]] === {{Status}} *{{LockHide|Autodesk Sketchbook Indonesia}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:30, 1 May 2020 (UTC) === Global lock for [[User:Xiplus|Xiplus]] === {{Status}} *{{LockHide|Xiplus}} :Test. --[[User:A2093064-test|A2093064-test]] ([[User talk:A2093064-test|talk]]) 01:45, 2 May 2020 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for LuniZunie === {{Status}} <!-- Do not remove this template --> * {{LockHide|LuniZunie}} Long-term abuse [[User:LuniZunie|LuniZunie]] ([[User talk:LuniZunie|talk]]) 01:38, 13 June 2026 (UTC) === Global lock for Enbi === {{Status}} *{{LockHide|Enbi}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 22:06, 4 September 2026 (UTC) == See also == <!-- DO NOT EDIT UNDER THIS LINE --> {{/Archivefooter}} {{RF}} 9sxoqrt46o56dn61nhej7352afvnepj 760343 760342 2026-09-04T22:09:29Z Enbi 72574 760343 wikitext text/x-wiki {{mbox|type=content|text=Due to persistent vandalism and disruption on this page, this page is currently semi-protected indefinitely. Globally blocked or locked users should appeal to {{nospam|stewards|wikimedia.org}}.<br />If your account was locked with a reason "Compromised account", please contact {{nospam|ca|wikimedia.org}}.}} {{dynamite|title=:Steward requests/Global/Header}}{{NOINDEX}}__TOC__<br style="clear:both"/> == Requests for global (un)block == <span id="B"/> {{dynamite|title=:Steward requests/Global/block-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global block for [[Special:Contributions/126.24.177.199|126.24.177.199]] === {{Status}} * {{Luxotool|126.24.177.199}} :Open proxy - running [[en:OpenVPN]] on port 1767 and 1393. An initial 3-month block should do the job. --[[User:MrClog|MrClog]] ([[User talk:MrClog|talk]]) 08:29, 1 May 2020 (UTC) {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:UpsandDowns1234|<span style="color:green;">Ups</span>]] and [[User talk:UpsandDowns1234|<span style="color:red;">Downs</span>]] ([[Special:Contributions/UpsandDowns1234|↕]]) 18:55, 18 May 2020 (UTC) === Global lock for [username hidden] === {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:Awesome Aasim|Awesome Aasim]] ([[User talk:Awesome Aasim|talk]]) 19:21, 18 May 2020 (UTC) == Requests for global (un)lock and (un)hiding == <span id="L"/> {{dynamite|title=:Steward requests/Global/lock-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global lock for [[User:Kshitij pandey 007|Kshitij pandey 007]] === {{Status}} *{{LockHide|Kshitij pandey 007}} :Constant vandalism. ---'''[[User:J ansari|<span style="background:#5d9731; color:white;padding:2px;">J. Ansari</span>]] [[User talk:J ansari|<span style="background:#1049AB; color:white; padding:2px;">Talk</span>]]''' 09:25, 1 May 2020 (UTC) ::{{re|J ansari}} Could this not be handled locally in Hindi Wikipedia? --[[User:Green Giant|Green Giant]] ([[User talk:Green Giant|talk]]) 18:13, 1 May 2020 (UTC) ===Global lock for LocoWiki=== {{status|notdone}} *{{lockHide|LocoWiki}} ;Evidence: *[[en:Category:Wikipedia_sockpuppets_of_LocoWiki]] *[[es:Wikipedia:Tablón de anuncios de los bibliotecarios/Portal/Archivo/Miscelánea/Actual#Caso: LocoWiki]] LTA for self-promotion crosswiki Regards!!!! [[User:Ezarate|Esteban]] ([[User talk:Ezarate|talk]]) 12:56, 1 May 2020 (UTC) :{{notdone}} No evidence of cross wiki abuse? [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:19, 1 May 2020 (UTC) === Global lock for [[User:Sriramakoti|Sriramakoti]] === {{Status|done}} *{{LockHide|Sriramakoti}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 13:56, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:16, 1 May 2020 (UTC) === Global lock for [[User:JosePenj|JosePenj]] === {{Status|notdone}} *{{MultiLock|JosePenj|Arman643543|Arman115786}} :Abusing multiple account. They created same topic article (James Richman) in xwiki and these account created few day before. --[[User:SCP-2000|<span style="color: #383838;">'''SCP'''</span>]][[User talk:SCP-2000|<span style="color: #242424;">'''-20'''</span>]][[Special:Contributions/SCP-2000|<span style="color: #080808;">'''00'''</span>]] 15:22, 1 May 2020 (UTC) :{{notdone}} No evidence of abuse - accounts are not blocked anywhere. [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:24, 1 May 2020 (UTC) === Global lock/unlock for [[User:Minhaj2288|Minhaj2288]] === {{status|done}} <!-- do not remove this template --> * {{LockHide|Minhaj2288}} * Crosswiki spammer --[[User:Seewolf|Harald Krichel]] ([[User talk:Seewolf|talk]]) 17:45, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:09, 1 May 2020 (UTC) === Global lock for [[User:AugustaFeaster7|AugustaFeaster7]] === {{Status|done}} *{{LockHide|AugustaFeaster7}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:29, 1 May 2020 (UTC) ::{{done}}--[[User:Sakretsu|Sakretsu]] ([[User talk:Sakretsu|炸裂]]) 21:35, 1 May 2020 (UTC) === Global lock for [[User:Autodesk Sketchbook Indonesia|Autodesk Sketchbook Indonesia]] === {{Status}} *{{LockHide|Autodesk Sketchbook Indonesia}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:30, 1 May 2020 (UTC) === Global lock for [[User:Xiplus|Xiplus]] === {{Status}} *{{LockHide|Xiplus}} :Test. --[[User:A2093064-test|A2093064-test]] ([[User talk:A2093064-test|talk]]) 01:45, 2 May 2020 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for LuniZunie === {{Status}} <!-- Do not remove this template --> * {{LockHide|LuniZunie}} Long-term abuse [[User:LuniZunie|LuniZunie]] ([[User talk:LuniZunie|talk]]) 01:38, 13 June 2026 (UTC) == See also == <!-- DO NOT EDIT UNDER THIS LINE --> {{/Archivefooter}} {{RF}} 26ozfndfkzll8377lz7z6uqnbh65wbi 760344 760343 2026-09-04T22:09:38Z Enbi 72574 Requesting global lock for [[Special:CentralAuth/$1|$1]] (Fortress-Beta) 760344 wikitext text/x-wiki {{mbox|type=content|text=Due to persistent vandalism and disruption on this page, this page is currently semi-protected indefinitely. Globally blocked or locked users should appeal to {{nospam|stewards|wikimedia.org}}.<br />If your account was locked with a reason "Compromised account", please contact {{nospam|ca|wikimedia.org}}.}} {{dynamite|title=:Steward requests/Global/Header}}{{NOINDEX}}__TOC__<br style="clear:both"/> == Requests for global (un)block == <span id="B"/> {{dynamite|title=:Steward requests/Global/block-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global block for [[Special:Contributions/126.24.177.199|126.24.177.199]] === {{Status}} * {{Luxotool|126.24.177.199}} :Open proxy - running [[en:OpenVPN]] on port 1767 and 1393. An initial 3-month block should do the job. --[[User:MrClog|MrClog]] ([[User talk:MrClog|talk]]) 08:29, 1 May 2020 (UTC) {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:UpsandDowns1234|<span style="color:green;">Ups</span>]] and [[User talk:UpsandDowns1234|<span style="color:red;">Downs</span>]] ([[Special:Contributions/UpsandDowns1234|↕]]) 18:55, 18 May 2020 (UTC) === Global lock for [username hidden] === {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:Awesome Aasim|Awesome Aasim]] ([[User talk:Awesome Aasim|talk]]) 19:21, 18 May 2020 (UTC) == Requests for global (un)lock and (un)hiding == <span id="L"/> {{dynamite|title=:Steward requests/Global/lock-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global lock for [[User:Kshitij pandey 007|Kshitij pandey 007]] === {{Status}} *{{LockHide|Kshitij pandey 007}} :Constant vandalism. ---'''[[User:J ansari|<span style="background:#5d9731; color:white;padding:2px;">J. Ansari</span>]] [[User talk:J ansari|<span style="background:#1049AB; color:white; padding:2px;">Talk</span>]]''' 09:25, 1 May 2020 (UTC) ::{{re|J ansari}} Could this not be handled locally in Hindi Wikipedia? --[[User:Green Giant|Green Giant]] ([[User talk:Green Giant|talk]]) 18:13, 1 May 2020 (UTC) ===Global lock for LocoWiki=== {{status|notdone}} *{{lockHide|LocoWiki}} ;Evidence: *[[en:Category:Wikipedia_sockpuppets_of_LocoWiki]] *[[es:Wikipedia:Tablón de anuncios de los bibliotecarios/Portal/Archivo/Miscelánea/Actual#Caso: LocoWiki]] LTA for self-promotion crosswiki Regards!!!! [[User:Ezarate|Esteban]] ([[User talk:Ezarate|talk]]) 12:56, 1 May 2020 (UTC) :{{notdone}} No evidence of cross wiki abuse? [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:19, 1 May 2020 (UTC) === Global lock for [[User:Sriramakoti|Sriramakoti]] === {{Status|done}} *{{LockHide|Sriramakoti}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 13:56, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:16, 1 May 2020 (UTC) === Global lock for [[User:JosePenj|JosePenj]] === {{Status|notdone}} *{{MultiLock|JosePenj|Arman643543|Arman115786}} :Abusing multiple account. They created same topic article (James Richman) in xwiki and these account created few day before. --[[User:SCP-2000|<span style="color: #383838;">'''SCP'''</span>]][[User talk:SCP-2000|<span style="color: #242424;">'''-20'''</span>]][[Special:Contributions/SCP-2000|<span style="color: #080808;">'''00'''</span>]] 15:22, 1 May 2020 (UTC) :{{notdone}} No evidence of abuse - accounts are not blocked anywhere. [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:24, 1 May 2020 (UTC) === Global lock/unlock for [[User:Minhaj2288|Minhaj2288]] === {{status|done}} <!-- do not remove this template --> * {{LockHide|Minhaj2288}} * Crosswiki spammer --[[User:Seewolf|Harald Krichel]] ([[User talk:Seewolf|talk]]) 17:45, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:09, 1 May 2020 (UTC) === Global lock for [[User:AugustaFeaster7|AugustaFeaster7]] === {{Status|done}} *{{LockHide|AugustaFeaster7}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:29, 1 May 2020 (UTC) ::{{done}}--[[User:Sakretsu|Sakretsu]] ([[User talk:Sakretsu|炸裂]]) 21:35, 1 May 2020 (UTC) === Global lock for [[User:Autodesk Sketchbook Indonesia|Autodesk Sketchbook Indonesia]] === {{Status}} *{{LockHide|Autodesk Sketchbook Indonesia}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:30, 1 May 2020 (UTC) === Global lock for [[User:Xiplus|Xiplus]] === {{Status}} *{{LockHide|Xiplus}} :Test. --[[User:A2093064-test|A2093064-test]] ([[User talk:A2093064-test|talk]]) 01:45, 2 May 2020 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for LuniZunie === {{Status}} <!-- Do not remove this template --> * {{LockHide|LuniZunie}} Long-term abuse [[User:LuniZunie|LuniZunie]] ([[User talk:LuniZunie|talk]]) 01:38, 13 June 2026 (UTC) === Global lock for Enbi === {{Status}} *{{LockHide|Enbi}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 22:09, 4 September 2026 (UTC) == See also == <!-- DO NOT EDIT UNDER THIS LINE --> {{/Archivefooter}} {{RF}} es5yhgz8thpncgr0zbepi2pvs1ef4nx 760345 760344 2026-09-04T22:11:51Z Enbi 72574 760345 wikitext text/x-wiki {{mbox|type=content|text=Due to persistent vandalism and disruption on this page, this page is currently semi-protected indefinitely. Globally blocked or locked users should appeal to {{nospam|stewards|wikimedia.org}}.<br />If your account was locked with a reason "Compromised account", please contact {{nospam|ca|wikimedia.org}}.}} {{dynamite|title=:Steward requests/Global/Header}}{{NOINDEX}}__TOC__<br style="clear:both"/> == Requests for global (un)block == <span id="B"/> {{dynamite|title=:Steward requests/Global/block-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global block for [[Special:Contributions/126.24.177.199|126.24.177.199]] === {{Status}} * {{Luxotool|126.24.177.199}} :Open proxy - running [[en:OpenVPN]] on port 1767 and 1393. An initial 3-month block should do the job. --[[User:MrClog|MrClog]] ([[User talk:MrClog|talk]]) 08:29, 1 May 2020 (UTC) {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:UpsandDowns1234|<span style="color:green;">Ups</span>]] and [[User talk:UpsandDowns1234|<span style="color:red;">Downs</span>]] ([[Special:Contributions/UpsandDowns1234|↕]]) 18:55, 18 May 2020 (UTC) === Global lock for [username hidden] === {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:Awesome Aasim|Awesome Aasim]] ([[User talk:Awesome Aasim|talk]]) 19:21, 18 May 2020 (UTC) == Requests for global (un)lock and (un)hiding == <span id="L"/> {{dynamite|title=:Steward requests/Global/lock-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global lock for [[User:Kshitij pandey 007|Kshitij pandey 007]] === {{Status}} *{{LockHide|Kshitij pandey 007}} :Constant vandalism. ---'''[[User:J ansari|<span style="background:#5d9731; color:white;padding:2px;">J. Ansari</span>]] [[User talk:J ansari|<span style="background:#1049AB; color:white; padding:2px;">Talk</span>]]''' 09:25, 1 May 2020 (UTC) ::{{re|J ansari}} Could this not be handled locally in Hindi Wikipedia? --[[User:Green Giant|Green Giant]] ([[User talk:Green Giant|talk]]) 18:13, 1 May 2020 (UTC) ===Global lock for LocoWiki=== {{status|notdone}} *{{lockHide|LocoWiki}} ;Evidence: *[[en:Category:Wikipedia_sockpuppets_of_LocoWiki]] *[[es:Wikipedia:Tablón de anuncios de los bibliotecarios/Portal/Archivo/Miscelánea/Actual#Caso: LocoWiki]] LTA for self-promotion crosswiki Regards!!!! [[User:Ezarate|Esteban]] ([[User talk:Ezarate|talk]]) 12:56, 1 May 2020 (UTC) :{{notdone}} No evidence of cross wiki abuse? [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:19, 1 May 2020 (UTC) === Global lock for [[User:Sriramakoti|Sriramakoti]] === {{Status|done}} *{{LockHide|Sriramakoti}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 13:56, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:16, 1 May 2020 (UTC) === Global lock for [[User:JosePenj|JosePenj]] === {{Status|notdone}} *{{MultiLock|JosePenj|Arman643543|Arman115786}} :Abusing multiple account. They created same topic article (James Richman) in xwiki and these account created few day before. --[[User:SCP-2000|<span style="color: #383838;">'''SCP'''</span>]][[User talk:SCP-2000|<span style="color: #242424;">'''-20'''</span>]][[Special:Contributions/SCP-2000|<span style="color: #080808;">'''00'''</span>]] 15:22, 1 May 2020 (UTC) :{{notdone}} No evidence of abuse - accounts are not blocked anywhere. [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:24, 1 May 2020 (UTC) === Global lock/unlock for [[User:Minhaj2288|Minhaj2288]] === {{status|done}} <!-- do not remove this template --> * {{LockHide|Minhaj2288}} * Crosswiki spammer --[[User:Seewolf|Harald Krichel]] ([[User talk:Seewolf|talk]]) 17:45, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:09, 1 May 2020 (UTC) === Global lock for [[User:AugustaFeaster7|AugustaFeaster7]] === {{Status|done}} *{{LockHide|AugustaFeaster7}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:29, 1 May 2020 (UTC) ::{{done}}--[[User:Sakretsu|Sakretsu]] ([[User talk:Sakretsu|炸裂]]) 21:35, 1 May 2020 (UTC) === Global lock for [[User:Autodesk Sketchbook Indonesia|Autodesk Sketchbook Indonesia]] === {{Status}} *{{LockHide|Autodesk Sketchbook Indonesia}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:30, 1 May 2020 (UTC) === Global lock for [[User:Xiplus|Xiplus]] === {{Status}} *{{LockHide|Xiplus}} :Test. --[[User:A2093064-test|A2093064-test]] ([[User talk:A2093064-test|talk]]) 01:45, 2 May 2020 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for LuniZunie === {{Status}} <!-- Do not remove this template --> * {{LockHide|LuniZunie}} Long-term abuse [[User:LuniZunie|LuniZunie]] ([[User talk:LuniZunie|talk]]) 01:38, 13 June 2026 (UTC) == See also == <!-- DO NOT EDIT UNDER THIS LINE --> {{/Archivefooter}} {{RF}} 26ozfndfkzll8377lz7z6uqnbh65wbi 760346 760345 2026-09-04T22:11:57Z Enbi 72574 Requesting global lock for [[Special:CentralAuth/Enbi|Enbi]] (Fortress-Beta) 760346 wikitext text/x-wiki {{mbox|type=content|text=Due to persistent vandalism and disruption on this page, this page is currently semi-protected indefinitely. Globally blocked or locked users should appeal to {{nospam|stewards|wikimedia.org}}.<br />If your account was locked with a reason "Compromised account", please contact {{nospam|ca|wikimedia.org}}.}} {{dynamite|title=:Steward requests/Global/Header}}{{NOINDEX}}__TOC__<br style="clear:both"/> == Requests for global (un)block == <span id="B"/> {{dynamite|title=:Steward requests/Global/block-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global block for [[Special:Contributions/126.24.177.199|126.24.177.199]] === {{Status}} * {{Luxotool|126.24.177.199}} :Open proxy - running [[en:OpenVPN]] on port 1767 and 1393. An initial 3-month block should do the job. --[[User:MrClog|MrClog]] ([[User talk:MrClog|talk]]) 08:29, 1 May 2020 (UTC) {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:UpsandDowns1234|<span style="color:green;">Ups</span>]] and [[User talk:UpsandDowns1234|<span style="color:red;">Downs</span>]] ([[Special:Contributions/UpsandDowns1234|↕]]) 18:55, 18 May 2020 (UTC) === Global lock for [username hidden] === {{status}} * {{LockHide|Awesome Aasim|hidename=yes}} test [[User:Awesome Aasim|Awesome Aasim]] ([[User talk:Awesome Aasim|talk]]) 19:21, 18 May 2020 (UTC) == Requests for global (un)lock and (un)hiding == <span id="L"/> {{dynamite|title=:Steward requests/Global/lock-header}} <!-- Your requests go AT THE BOTTOM OF THE SECTION. Copy the request template above and fill in your information. --> === Global lock for [[User:Kshitij pandey 007|Kshitij pandey 007]] === {{Status}} *{{LockHide|Kshitij pandey 007}} :Constant vandalism. ---'''[[User:J ansari|<span style="background:#5d9731; color:white;padding:2px;">J. Ansari</span>]] [[User talk:J ansari|<span style="background:#1049AB; color:white; padding:2px;">Talk</span>]]''' 09:25, 1 May 2020 (UTC) ::{{re|J ansari}} Could this not be handled locally in Hindi Wikipedia? --[[User:Green Giant|Green Giant]] ([[User talk:Green Giant|talk]]) 18:13, 1 May 2020 (UTC) ===Global lock for LocoWiki=== {{status|notdone}} *{{lockHide|LocoWiki}} ;Evidence: *[[en:Category:Wikipedia_sockpuppets_of_LocoWiki]] *[[es:Wikipedia:Tablón de anuncios de los bibliotecarios/Portal/Archivo/Miscelánea/Actual#Caso: LocoWiki]] LTA for self-promotion crosswiki Regards!!!! [[User:Ezarate|Esteban]] ([[User talk:Ezarate|talk]]) 12:56, 1 May 2020 (UTC) :{{notdone}} No evidence of cross wiki abuse? [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:19, 1 May 2020 (UTC) === Global lock for [[User:Sriramakoti|Sriramakoti]] === {{Status|done}} *{{LockHide|Sriramakoti}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 13:56, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:16, 1 May 2020 (UTC) === Global lock for [[User:JosePenj|JosePenj]] === {{Status|notdone}} *{{MultiLock|JosePenj|Arman643543|Arman115786}} :Abusing multiple account. They created same topic article (James Richman) in xwiki and these account created few day before. --[[User:SCP-2000|<span style="color: #383838;">'''SCP'''</span>]][[User talk:SCP-2000|<span style="color: #242424;">'''-20'''</span>]][[Special:Contributions/SCP-2000|<span style="color: #080808;">'''00'''</span>]] 15:22, 1 May 2020 (UTC) :{{notdone}} No evidence of abuse - accounts are not blocked anywhere. [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:24, 1 May 2020 (UTC) === Global lock/unlock for [[User:Minhaj2288|Minhaj2288]] === {{status|done}} <!-- do not remove this template --> * {{LockHide|Minhaj2288}} * Crosswiki spammer --[[User:Seewolf|Harald Krichel]] ([[User talk:Seewolf|talk]]) 17:45, 1 May 2020 (UTC) ::{{done}} [[User:Ruslik0|Ruslik]] ([[User talk:Ruslik0|talk]]) 20:09, 1 May 2020 (UTC) === Global lock for [[User:AugustaFeaster7|AugustaFeaster7]] === {{Status|done}} *{{LockHide|AugustaFeaster7}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:29, 1 May 2020 (UTC) ::{{done}}--[[User:Sakretsu|Sakretsu]] ([[User talk:Sakretsu|炸裂]]) 21:35, 1 May 2020 (UTC) === Global lock for [[User:Autodesk Sketchbook Indonesia|Autodesk Sketchbook Indonesia]] === {{Status}} *{{LockHide|Autodesk Sketchbook Indonesia}} :Spam / spambot. --[[User:DannyS712|DannyS712]] ([[User talk:DannyS712|talk]]) 21:30, 1 May 2020 (UTC) === Global lock for [[User:Xiplus|Xiplus]] === {{Status}} *{{LockHide|Xiplus}} :Test. --[[User:A2093064-test|A2093064-test]] ([[User talk:A2093064-test|talk]]) 01:45, 2 May 2020 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for Enbi (script testing) === {{Status}} *{{LockHide|Enbi (script testing)}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 20:20, 18 April 2026 (UTC) === Global lock for LuniZunie === {{Status}} <!-- Do not remove this template --> * {{LockHide|LuniZunie}} Long-term abuse [[User:LuniZunie|LuniZunie]] ([[User talk:LuniZunie|talk]]) 01:38, 13 June 2026 (UTC) === Global lock for Enbi === {{Status}} *{{LockHide|Enbi}} Long-term abuse. <b style="font-family:Trebuchet MS">[[User:enbi|<span style="color:#E28C00">enbi</span>]] &#91;<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>&#93; • &#91;[[User talk:enbi|talk]]&#93;</b> 22:11, 4 September 2026 (UTC) == See also == <!-- DO NOT EDIT UNDER THIS LINE --> {{/Archivefooter}} {{RF}} cdlrem2it4qfebj2ybms5m4qc07wgul Asian Paints 0 122452 760365 688460 2026-09-05T00:03:34Z InternetArchiveBot 34092 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 760365 wikitext text/x-wiki    '''ఏషియన్ పెయింట్స్ లిమిటెడ్''' అనేది [[ముంబై|ముంబైలో]] ప్రధాన కార్యాలయం ఉన్న భారతీయ బహుళజాతి పెయింట్ కంపెనీ. <ref name="Business Insider">{{Cite web|url=http://www.businessinsider.in/Independence-Day-Special-Started-by-four-Mumbaikers-how-Asian-Paints-is-today-one-of-Indias-most-valued-companies/articleshow/53634906.cms|title=Started by four Mumbaikers, how Asian Paints is today one of India's most valued companies|access-date=18 November 2016|archive-date=21 August 2017|archive-url=https://web.archive.org/web/20170821170144/http://www.businessinsider.in/Independence-Day-Special-Started-by-four-Mumbaikers-how-Asian-Paints-is-today-one-of-Indias-most-valued-companies/articleshow/53634906.cms|url-status=dead}}</ref> పెయింట్‌లు, పూతలు, గృహాలంకరణకు సంబంధించిన ఉత్పత్తులు, స్నానపు అమరికలు , సంబంధిత సేవలను అందించడం, విక్రయించడం , పంపిణీ చేసే వ్యాపారంలో కంపెనీ నిమగ్నమై ఉంది. ఆసియన్ పెయింట్స్ భారతదేశంలో అతిపెద్ద , ఆసియాలో మూడవ అతిపెద్ద పెయింట్స్ కార్పొరేషన్. <ref name="IBTimes">{{Cite web|url=http://www.ibtimes.co.in/asian-paints-hindustan-unilever-top-innovative-indian-firms-forbes-list-691157|title=Asian Paints, Hindustan Unilever top innovative Indian firms on Forbes' list|access-date=18 November 2016}}</ref> <ref name="Ecot">{{Cite web|url=http://economictimes.indiatimes.com/markets/stocks/news/shares-of-asian-paints-jump-7-on-good-q1-results/articleshow/53427210.cms|title=Shares of Asian Paints jump 7% on good Q1 results|access-date=18 November 2016}}</ref> <ref name="Hindu">{{Cite web|url=http://www.thehindu.com/news/cities/Visakhapatnam/local-people-demand-jobs-in-asian-paints-proposed-plant/article8791029.ece|title=Local people demand jobs in Asian Paints' proposed plant|access-date=18 November 2016}}</ref> ఆసియన్ పెయింట్స్ బెర్గర్ ఇంటర్నేషనల్ యొక్క హోల్డింగ్ కంపెనీ. <ref name="Ecot2">{{Cite web|url=http://economictimes.indiatimes.com/markets/stocks/news/asian-paints-acquires-96-48-stake-in-berger-open-offer-ends/articleshow/25600460.cms|title=Asian Paints acquires 96.48% stake in Berger; open offer ends|access-date=18 November 2016}}</ref> కంపెనీ 2019-20 ఆర్థిక సంవత్సరంలో ₹ 17,194.1 [[కోటి|కోట్ల]] ఆదాయాన్ని , ₹ 2,654 కోట్ల లాభాన్ని ఆర్జించింది. కంపెనీ తయారీ కార్యకలాపాలు భారతదేశంతో సహా ప్రపంచంలోని 15 దేశాలను కలిగి ఉన్నాయి, [[భారత ఉపఖండము|భారత ఉపఖండం]] , మధ్యప్రాచ్యంలో గణనీయమైన ఉనికిని కలిగి ఉన్నాయి. <ref>{{Cite web|title=Annual report 2019-20|url=https://www.asianpaints.com/content/dam/asianpaints/website/secondary-navigation/investors/annual-reports/Asian%20Paints%20Annual%20Report_Web.pdf|url-status=dead|access-date=2021-10-21|archive-date=2020-09-26|archive-url=https://web.archive.org/web/20200926225804/https://www.asianpaints.com/content/dam/asianpaints/website/secondary-navigation/investors/annual-reports/Asian%20Paints%20Annual%20Report_Web.pdf}}</ref> సంస్థ Gaiwadi, ఒక గ్యారేజీలో ప్రారంభించారు గిర్గామ్, [[ముంబై|ముంబై]] నాలుగు స్నేహితులు చంపక్ Choksey, Chimanlal Choksi, సూర్యకాంత్ డానీ అరవింద్ వకీల్ ద్వారా. వీరందరూ [[జైన మతము|జైన్]] కుటుంబాలకు చెందినవారు , ఫిబ్రవరి 1945 లో కంపెనీని స్థాపించారు. రెండవ [[రెండవ ప్రపంచ యుద్ధం|ప్రపంచ యుద్ధం]] , [[క్విట్ ఇండియా ఉద్యమం|1942 క్విట్ ఇండియా ఉద్యమం]] సమయంలో, పెయింట్ దిగుమతులపై తాత్కాలిక నిషేధం మార్కెట్లో విదేశీ కంపెనీలు , షాలిమార్ పెయింట్స్ మాత్రమే మిగిలిపోయింది. ఏషియన్ పెయింట్స్ మార్కెట్‌ను చేపట్టింది , 23 [[కోటి|కోట్లని]] నివేదించింది, కానీ కేవలం 2% PBT మార్జిన్‌తో. 1967 నాటికి, ఇది దేశంలో ప్రముఖ పెయింట్స్ తయారీదారుగా మారింది. <ref name="Unusual">{{Cite book|url=https://books.google.com/books?id=UL3BDAAAQBAJ|title=The Unusual Billionaires|first=Saurabh|last=Mukherjea|publisher=Penguin UK|year=2016|isbn=9789386057679}}</ref> <ref name="Club">{{Cite book|url=https://books.google.com/books?id=YiLBD_IpgIkC|title=India's Global Wealth Club: The Stunning Rise of Its Billionaires and Their Secrets of Success|publisher=John Wiley & Sons|pages=211|first=Geoff|last=Hiscock|year=2008|isbn=9780470822388}}</ref> నాలుగు కుటుంబాలు కలిసి కంపెనీ మెజారిటీ వాటాలను కలిగి ఉన్నాయి. కానీ 1990 లలో కంపెనీ భారతదేశానికి మించి విస్తరించినప్పుడు ప్రపంచ హక్కులపై వివాదాలు ప్రారంభమయ్యాయి. వివాదాలు ఫలితంగా చోక్సే వారి 13.7% వాటాలను విక్రయించి 1997 లో నిష్క్రమించారు. 1997 జూలైలో చంపక్లాల్ మరణించాడు , అతని కుమారుడు అతుల్ బాధ్యతలు స్వీకరించారు. బ్రిటిష్ కంపెనీ ఇంపీరియల్ కెమికల్ ఇండస్ట్రీస్‌తో సహకార చర్చలు విఫలమైన తర్వాత, చోక్సీ షేర్లను మిగిలిన మూడు కుటుంబాలు , యూనిట్ ట్రస్ట్ ఆఫ్ ఇండియా పరస్పరం కొనుగోలు చేశాయి. {{As of|2008}} , చోక్సీ, డాని , వాకిల్ కుటుంబాలు 47.81%వాటాను కలిగి ఉన్నాయి. <ref name="Club"/> == తయారీ స్థానాలు <ref>{{Cite web|title=https://www.asianpaints.com/content/ap/en/home/about-us.html|url=https://www.asianpaints.com/content/ap/en/home/about-us.html|access-date=2021-06-07|website=www.asianpaints.com|language=en}}</ref> == సంస్థ దాని అనుబంధ సంస్థలతో పాటు 15 దేశాలలో 26 తయారీ కేంద్రాలను కలిగి ఉంది, ప్రపంచవ్యాప్తంగా 60 దేశాలకు పైగా వినియోగదారులకు సేవలు అందిస్తోంది. {| class="wikitable" |+ !S.No. ! దేశాలు ! తయారీ ప్లాంట్ల సంఖ్య ! స్థానం/ఆపరేటింగ్ బ్రాండ్ |- | 1 | భారతదేశం | 10 | అంకేశ్వర్ & సరిగాం ( [[గుజరాత్]] ), [[పటాన్‌చెరు|పటాన్చెరు]] ( [[తెలంగాణ]] ), కస్నా ( [[ఉత్తరప్రదేశ్]] ), శ్రీపెరంబుదూర్ ( [[తమిళనాడు]] ), [[రోహ్‌తక్]] ( [[హర్యానా]] ), ఖండాల & తలోజా ( [[మహారాష్ట్ర]] ), [[మైసూరు]] ( [[కర్ణాటక]] ), [[విశాఖపట్నం]] ( [[ఆంధ్రప్రదేశ్]] ) |- | 2 | [[శ్రీలంక]] | 2 | ఆసియన్ పెయింట్స్ కాజ్‌వే (2) |- | 3 | [[నేపాల్]] | 2 | ఆసియా పెయింట్స్ |- | 4 | [[బంగ్లాదేశ్]] | 1 | ఆసియా పెయింట్స్ |- | 5 | [[ఇండోనేషియా]] | 1 | ఆసియా పెయింట్స్ |- | 6 | [[ఫిజీ|ఫిజి]] | 1 | ఆప్కో కోటింగ్‌లు & టబ్‌మ్యాన్స్ |- | 7 | సమోవా దీవులు | 1 | టబ్‌మన్స్ |- | 8 | [[వానువాటు|వనాటు]] | | ఆప్కో పూతలు |- | 9 | [[సోలమన్ దీవులు]] | | ఆప్కో పూతలు |- | 10 | [[ఒమన్]] | 1 | ఆసియన్ పెయింట్స్ బెర్గర్ |- | 11 | [[బహ్రయిన్|బహ్రెయిన్]] | 1 | ఆసియన్ పెయింట్స్ బెర్గర్ |- | 12 | [[దుబాయ్]] | 1 | ఆసియన్ పెయింట్స్ బెర్గర్ |- | 13 | [[ఈజిప్టు|ఈజిప్ట్]] | 2 | SCIB పెయింట్స్ |- | 14 | [[ఇథియోపియా]] | 3 | కడిస్కో ఆసియన్ పెయింట్స్ |- | colspan="2" | మొత్తం | 26 | |} == యాజమాన్య నిర్మాణం == సెక్యూరిటీస్ ఎక్స్ఛేంజ్ కమిషన్ (SEC) ద్వారా పెట్టుబడి పెట్టే సంస్థలో 12 సంస్థాగత యజమానులు , వాటాదారులు ఉన్నారు. అతిపెద్ద వాటాదారులలో బ్రిడ్జ్ బిల్డర్ ఇంటర్నేషనల్ ఈక్విటీ ఫండ్ , టచ్‌స్టోన్ సాండ్స్ క్యాపిటల్ ఎమర్జింగ్ మార్కెట్స్ గ్రోత్ ఫండ్ ఉన్నాయి. <ref>{{Cite news|last=Stuff|first=Publication|title=ASIANPAINT Institutional Ownership - ASIAN PAINTS LTD. Stock|language=en|work=fintel.io|url=https://fintel.io/so/in/asianpaint|access-date=2 May 2020|archive-date=21 October 2021|archive-url=https://web.archive.org/web/20211021084749/https://fintel.io/so/in/asianpaint|url-status=dead}}</ref> == నాయకత్వ బృందం == అమిత్ సింగిల్ ప్రస్తుతం ఆసియన్ పెయింట్స్ మేనేజింగ్ డైరెక్టర్ , CEO. <ref>{{Cite web|title=Company's website|url=https://www.asianpaints.com/content/dam/annual-report/pdf/leadershipteam.pdf|url-status=live}}</ref> == మార్కెటింగ్ , ప్రకటన == 1950 లలో, కంపెనీ "ఉతికి లేక కడిగివేయగల డిస్టెంపర్ " ను ప్రారంభించింది, ఇది చౌకగా ఉండే పొడిగా ఉండే డిస్టెంపర్ , అత్యంత ఖరీదైన ప్లాస్టిక్ ఎమల్షన్‌ల మధ్య సమతుల్యత. వారి బ్రాండ్ ట్రాక్టర్ డిస్టెంపర్‌ని ప్రమోట్ చేస్తూ, కంపెనీ వారి ప్రకటనలలో "నిగ్రహాన్ని కోల్పోకండి, ట్రాక్టర్ డిస్టెంపర్ ఉపయోగించండి" అని ఉపయోగించింది. <ref name="Unusual"/> 1954 లో, "గట్టు" - చేతిలో పెయింట్ బకెట్ ఉన్న ఒక కొంటె బాలుడు - మస్కట్‌గా ప్రారంభించబడింది. [[ఆర్.కె.లక్ష్మణ్|RK లక్ష్మణ్]] ద్వారా సృష్టించబడిన, మస్కట్ మధ్యతరగతి వర్గాలకు ఆకర్షణీయంగా ఉంది. <ref name="Gattu">{{Cite web|url=http://www.business-standard.com/article/management/40-years-ago-and-now-gattu-s-antics-coloured-asian-paints-future-115021201710_1.html|title=40 years ago...And now: Gattu's antics coloured Asian Paints' future|last=Pinto|first=Viveat|date=12 February 2015|website=Business Standard|access-date=19 November 2016}}</ref> అతను 1970 ల వరకు , 1990 ల వరకు ముద్రణ ప్రకటనలు , ప్యాకేజింగ్‌లో మాత్రమే ఉపయోగించబడ్డాడు, టెలివిజన్ ప్రకటనలలో కూడా కనిపించాడు. గృహ యజమానుల యొక్క తుది వినియోగదారులకు చిత్రకారుల సరుకుల నేతృత్వంలోని వ్యాపారాన్ని తీసుకురావడానికి గట్టు సహాయపడింది. <ref name="Gattu"/> ఓగిల్వి &amp;amp; మాథర్, ఏషియన్ పెయింట్స్‌తో అనుబంధించబడిన ప్రకటనల ఏజెన్సీ, 1980 లలో పండుగ సందర్భాలలో దృష్టి సారించడం ద్వారా మార్కెటింగ్ వ్యూహాన్ని ప్రారంభించింది, "హర్ ఘర్ కుచ్ కెహతా హై" (ప్రతి ఇంటికి ఏదో ఒకటి చెబుతుంది). పండుగలు , వివాహాలు , పిల్లల పుట్టుక వంటి ముఖ్యమైన జీవిత సంఘటనలకు సంబంధించి, భావోద్వేగ స్థాయిలో అనుసంధానం చేయడం ద్వారా గృహాలకు రంగులు వేసే సందర్భంగా కంపెనీ ప్రచారం చేసింది. 1990 వ దశకంలో, ప్రకటనలు ఇంటి వెలుపలి వైపు దృష్టి సారించాయి, పెయింట్‌లు వెలుపలి భాగాలను ఎలా టైమ్‌లెస్‌గా ఉంచుతాయనే దానిపై దృష్టి పెట్టాయి. <ref name="Gattu" /> ఈ సంస్థ 2000 వ దశకంలో తన కార్పొరేట్ గుర్తింపును పునరుద్ధరించింది , గట్టును వారి చిహ్నంగా గుర్తించింది , తరువాత దాని "ఆసియన్ పెయింట్స్" లోగోను చిన్న "AP" మెమోనిక్‌గా మార్చింది. <ref name="Gattu" /> == షేర్‌హోల్డింగ్ నమూనా == ''31 మార్చి 2021 నాటికి'' {| class="wikitable" |+ !వాటాదారుల వర్గం ! వాటాదారుల సంఖ్య ! షేర్‌హోల్డింగ్ |- | ప్రమోటర్ & ప్రమోటర్ గ్రూప్ | 81 | 52.7925% |- | ప్రజా | 4,99,104 | 47.2075% |- | మొత్తం | 4,99,185 | 100% |} k6oxakzh3y0y5u1qbus76irn7c4f4d9 Mwbot-rs/Save 0 122864 760244 759065 2026-09-04T12:06:27Z Mwbot-rs test 52001 Test suite edit 760244 wikitext text/x-wiki It has been 1788523587 seconds since the epoch. 0nb9zs4myrgsible9mhze6ypi3j2avo 760245 760244 2026-09-04T12:06:38Z Mwbot-rs test 52001 Test suite edit 760245 wikitext text/x-wiki It has been 1788523598 seconds since the epoch. eta1jrq6y3qpt4veo0h4vasr6uy550e 760246 760245 2026-09-04T12:06:43Z Mwbot-rs test 52001 Test suite edit 760246 wikitext text/x-wiki It has been 1788523603 seconds since the epoch. gg1d4ttkc6et193njopaj5t60d69boa 760247 760246 2026-09-04T12:07:24Z Mwbot-rs test 52001 Test suite edit 760247 wikitext text/x-wiki It has been 1788523643 seconds since the epoch. m2xqdklohrb2dyy34qpbv0rw3oey9gs User:Nardog/sandbox5.js 2 122986 760333 759825 2026-09-04T21:54:28Z Nardog 40946 760333 javascript text/javascript window.moveHistoryDialog?.close(); $('#mw-notification-area').empty(); $('head > style').filter(function () { return this.textContent.includes('.movehistory'); }).remove(); mw.loader.using([ 'mediawiki.api', 'mediawiki.util', 'mediawiki.Title', 'mediawiki.DateFormatter', 'oojs-ui-windows', 'oojs-ui-widgets', 'mediawiki.widgets', 'mediawiki.widgets.DateInputWidget', 'oojs-ui.styles.icons-interactions', 'mediawiki.interface.helpers.styles' ], require => { mw.loader.addStyleTag(`.movehistory .oo-ui-window-body { padding: 1em; display: flex; justify-content: center; } .movehistory form { flex-grow: 1; } .movehistory .wikitable { width: 100%; margin-top: 0; word-break: break-word; } .movehistory-date, .movehistory-user { white-space: nowrap; } .movehistory-title { text-align: center; } .movehistory-comment { vertical-align: top; max-width: 16em; }`); let dialog; let DateFormatter = require('mediawiki.DateFormatter'); let wrapDate = (s, isWiki) => { let strs; let formatter = isWiki ? DateFormatter.forSiteZone() : DateFormatter; let date = new Date(s); let dateTime = formatter.formatTimeAndDate(date); let time = formatter.formatTime(date); if (dateTime.startsWith(time)) { time += dateTime.slice(time.length).replace(/^(\p{P}*).*/u, '$1'); strs = [time, dateTime.slice(time.length).trimStart()]; } else if (dateTime.endsWith(time)) { strs = [dateTime.slice(0, -time.length).trimEnd(), time]; } else { return [dateTime]; } strs = strs.map(str => isWiki ? nowiki(str) : document.createTextNode(str)); strs.splice(1, 0, '<br>'); return strs; }; let nowiki = s => s.replace( /["&'<=>\[\]{|}]|:(?=\/\/)|_(?=_)|~(?=~~)/g, m => '&#' + m.codePointAt(0) + ';' ); let articlePath = mw.config.get('wgArticlePath').replace(/\$1.*/, ''); let getPath = (pathname, search, hash) => { let s = ''; if (pathname?.startsWith(articlePath)) { s = decodeURIComponent(pathname.slice(articlePath.length)); } else if (search) { let title = mw.util.getParamValue('title', search); if (title) { s = title; } } if (hash) { s += mw.util.percentDecodeFragment(hash); } return s.replace(/_/g, ' '); }; let api = new mw.Api({ // userAgent: 'MoveHistory (https://en.wikipedia.org/wiki/User:Nardog/MoveHistory)' }); let arrow = document.dir === 'rtl' ? ' ← ' : ' → '; class MoveHistorySearch { constructor(page, dir, since, until) { this.$status = $('<div>').appendTo(dialog.$results.empty()); this.page = page; this.ascending = dir === 'newer'; let sinceTs = (since || '2005-06-25') + 'T00:00:00Z'; let untilTs; if (until) { untilTs = until + 'T23:59:59Z'; } this.params = { action: 'query', titles: page, prop: 'revisions', rvstart: this.ascending ? sinceTs : untilTs, rvend: this.ascending ? untilTs : sinceTs, rvdir: dir, rvprop: 'sha1|timestamp|user|comment', rvlimit: 'max', formatversion: 2 }; this.revCount = 0; this.candidates = []; this.titles = {}; this.noRedirLinks = new WeakSet(); this.moves = []; this.start(); } start() { this.setBusy(true); dialog.actions.setMode('searching'); this.i = 0; this.paused = false; this.doNext(); } doNext() { if (!this.paused && this.candidates.length) { this.loadMoves(); } else if (!this.paused && !this.complete && this.i < 4) { this.loadRevs(); } else { this.finish(); } } async loadRevs() { this.i++; this.setStatus(`Loading history${ this.revCount ? this.ascending ? ' after ' + this.lastDate : ' before ' + this.firstDate : '' }...`); try { let response = await api.get(this.params); let revs = response.query?.pages?.[0]?.revisions; if (revs) { this.processRevs(revs); } this.params.rvcontinue = response.continue?.rvcontinue; if (!this.params.rvcontinue) { this.complete = response.batchcomplete; } this.doNext(); } catch (error) { let errorMsg = error?.error?.info || error; this.finish(`Error loading revisions${errorMsg ? ': ' + errorMsg : ''}`); } } processRevs(revs) { this.revCount += revs.length; if (!this.ascending) { revs.reverse(); } revs.forEach(rev => { let comp = this.lastRev; this.lastRev = rev; if (!rev.comment || !rev.user || !rev.sha1 || !comp || comp.sha1 !== rev.sha1 ) { return; } let matches = rev.comment.match(/\[\[:?([^\]]+)\]\].+?\[\[:?([^\]]+)\]\]/); if (matches) { rev.matches = matches.slice(1); } this.candidates.push(rev); }); if (!this.ascending || !this.firstDate) { this.firstDate = DateFormatter.formatTimeAndDate(new Date(revs[0].timestamp)); } if (this.ascending || !this.lastDate) { this.lastDate = DateFormatter.formatTimeAndDate(new Date(this.lastRev.timestamp)); } } async loadMoves() { let rev = this.candidates[this.ascending ? 'shift' : 'pop'](); this.setStatus(`Seeing if there was a move at ${ DateFormatter.formatTimeAndDate(new Date(rev.timestamp)) }...`); let date = Date.parse(rev.timestamp) / 1000; try { (await api.get({ action: 'query', list: 'logevents', letype: 'move', lestart: date + 60, leend: date, leprop: 'details|title|user|parsedcomment', lelimit: 'max', formatversion: 2 })).query?.logevents?.reverse().some(le => { if (le.user !== rev.user || !rev.comment.includes(le.title)) return; let target = le?.params?.target_title; if (!target || !rev.comment.includes(target) || rev.matches && [le.title, target].some(s => !rev.matches.includes(s)) ) { return; } this.addMove({ date: rev.timestamp, offset: new Date(Date.parse(rev.timestamp) + 1000) .toISOString().slice(0, -5).replace(/\D/g, ''), from: le.title, to: target, user: le.user, comment: $.parseHTML(le.parsedcomment) }); return true; }); this.doNext(); } catch (error) { let errorMsg = error?.error?.info || error; this.finish(`Error loading moves${errorMsg ? ': ' + errorMsg : ''}`); } } addMove(move) { if (!this.moves.length) { this.lastName = this.ascending ? move.from : move.to; this.$trail = $('<p>').append(this.makeLink(this.lastName)); this.$tbody = $('<tbody>'); this.$table = $('<table>').addClass('wikitable').append( $('<thead>').append( $('<tr>').append( $('<th>').attr('rowspan', 2).text('Date'), $('<th>').text(this.ascending ? 'From' : 'To'), $('<th>').attr('rowspan', 2).text('Performer'), $('<th>').attr('rowspan', 2).text('Comment') ), $('<tr>').append( $('<th>').text(this.ascending ? 'To' : 'From') ) ), this.$tbody ); this.$status.after('<hr>', this.$trail, this.$table); } if (this.ascending) { if (this.lastName !== move.from) { this.$trail.append(arrow + '?' + arrow, this.makeLink(move.from)); } this.$trail.append(arrow, this.makeLink(move.to)); this.lastName = move.to; } else { if (this.lastName !== move.to) { this.$trail.prepend(this.makeLink(move.to), arrow + '?' + arrow); } this.$trail.prepend(this.makeLink(move.from), arrow); this.lastName = move.from; } this.$tbody.append( $('<tr>').append( $('<td>').attr({ class: 'movehistory-date', rowspan: 2 }).append( $('<a>').attr({ href: mw.util.getUrl(this.page, { action: 'history', offset: move.offset }), title: 'See history up to this move', target: '_blank' }).append(wrapDate(move.date)) ), $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.from : move.to) ), $('<td>').attr({ class: 'movehistory-user', rowspan: 2 }).append( this.makeLink('User:' + move.user, move.user, true), '<br>', $('<span>').addClass('mw-changeslist-links').append( $('<span>').append(this.makeLink('User talk:' + move.user, 'talk', true)), $('<span>').append(this.makeLink('Special:Contributions/' + move.user, 'contribs', true)) ) ), $('<td>').attr({ class: 'movehistory-comment', rowspan: 2 }).append( $(move.comment).clone().attr('target', '_blank') ) ), $('<tr>').append( $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.to : move.from) ) ) ); dialog.setSize('large'); this.moves.push(move); } finish(error) { let count = this.moves.length; let complete = this.complete && !this.candidates.length; this.setStatus(error || `Found ${count} move${count === 1 ? '' : 's'} in ${ this.revCount.toLocaleString('en') } revisions${ this.revCount ? ` from ${this.firstDate} to ${this.lastDate}` : '' }.${ complete ? '' : ' Click Continue to inspect more revisions.' }`); this.setBusy(); this.mode = complete ? count ? 'found' : 'notFound' : count ? 'pausedFound' : 'paused'; dialog.actions.setMode(this.mode); if (!count) return; this.queryTitles( Object.entries(this.titles) .filter(([, v]) => !v.processed).map(([k]) => k) ); } setBusy(busy) { MoveHistoryDialog.static.escapable = !busy; dialog.$navigation.toggleClass('oo-ui-pendingElement-pending', !!busy); } setStatus(text) { this.$status.text(text); dialog.updateSize(); console.log(text); } makeLink(title, text, allowRedirect) { let obj; if (Object.hasOwn(this.titles, title)) { obj = this.titles[title]; } else { obj = { links: [] }; this.titles[title] = obj; if (title === this.page) { obj.classes = ['mw-selflink', 'selflink']; obj.processed = true; } } let params = obj.red && { action: 'edit', redlink: 1 } || !allowRedirect && obj.redirect && { redirect: 'no' }; let $link = $('<a>').attr({ href: mw.util.getUrl(obj.canonical || title, params), title: obj.canonical || title, target: '_blank' }).addClass(obj.classes).text(text || title); if (!allowRedirect && !obj.processed) { this.noRedirLinks.add($link[0]); } if (!obj.processed) { obj.links.push($link[0]); } return $link; } queryTitles(titles) { if (!titles.length) return; let curTitles = titles.slice(0, 50); curTitles.forEach(title => { this.titles[title].processed = true; }); let { query } = api.post({ action: 'query', titles: curTitles, prop: 'info', inprop: 'linkclasses', inlinkcontext: this.page, formatversion: 2 }, { headers: { 'Promise-Non-Write-API-Action': 1 } }); query?.normalized?.forEach(entry => { if (!Object.hasOwn(this.titles, entry.from)) return; let obj = this.titles[entry.from]; obj.canonical = entry.to; this.titles[entry.to] = obj; }); query?.pages?.forEach(page => { if (!Object.hasOwn(this.titles, page.title)) return; let obj = this.titles[page.title]; let classes = page.linkclasses || []; if (page.missing && !page.known) { classes.push('new'); obj.red = true; } else if (classes.includes('mw-redirect')) { obj.redirect = true; } if (classes.length) { obj.classes = classes; } }); curTitles.forEach(title => { let obj = this.titles[title]; let $links = $(obj.links).addClass(obj.classes); $links.attr('href', i => mw.util.getUrl( obj.canonical || title, obj.red && { action: 'edit', redlink: 1 } || obj.redirect && this.noRedirLinks.has($links[i]) && { redirect: 'no' } )); if (obj.canonical) { $links.attr('title', obj.canonical); } delete obj.links; }); this.queryTitles(titles.slice(50)); } copyResults() { let text = this.$trail.contents().get().map(n => ( n.tagName === 'A' ? `[[:${n.textContent}]]` : n.textContent )).join('') + ` {| class="wikitable plainlinks" ! rowspan="2" | Date ! ${this.ascending ? 'From' : 'To'} ! rowspan="2" | Performer ! rowspan="2" | Comment |- ! ${this.ascending ? 'To' : 'From'} ${this.moves.map(move => `|- | rowspan="2" style="white-space: nowrap;" | [{{fullurl:${this.page}|action=history&offset=${move.offset}}} ${wrapDate(move.date, true).join('')}] | style="text-align: center;" | ${ this.titles[this.ascending ? move.from : move.to] && this.titles[this.ascending ? move.from : move.to].redirect ? `[{{fullurl:${this.ascending ? move.from : move.to}|redirect=no}} ${this.ascending ? move.from : move.to}]` : `[[:${this.ascending ? move.from : move.to}]]` } | rowspan="2" style="white-space: nowrap;" | [[User:${move.user}|${move.user}]]<br>([[User talk:${move.user}|talk]] &#124; [[Special:Contributions/${move.user}|contribs]]) | rowspan="2" ${ move.comment.length ? 'style="vertical-align: top;" | ' + move.comment.map(n => ( n.tagName === 'A' ? `[[:${ n.classList.contains('extiw') ? n.title : getPath(n.pathname, n.search, n.hash) }|${nowiki(n.textContent)}]]` : nowiki(n.textContent) )).join('') : '|' } |- | style="text-align: center;" | ${ this.titles[this.ascending ? move.to : move.from] && this.titles[this.ascending ? move.to : move.from].redirect ? `[{{fullurl:${this.ascending ? move.to : move.from}|redirect=no}} ${this.ascending ? move.to : move.from}]` : `[[:${this.ascending ? move.to : move.from}]]` } `).join('')}|}`; let $textarea = $('<textarea>').attr({ readonly: '', style: 'position:fixed;top:-100%' }).val(text).appendTo(document.body); $textarea[0].select(); let copied; try { copied = document.execCommand('copy'); } catch (e) {} $textarea.remove(); if (copied) { mw.notify('Copied'); } else { mw.notify('Copy failed', { type: 'error' }); } } } function MoveHistoryDialog(config) { MoveHistoryDialog.parent.call(this, config); this.$element.addClass('movehistory'); } OO.inheritClass(MoveHistoryDialog, OO.ui.ProcessDialog); MoveHistoryDialog.static.name = 'moveHistoryDialog'; MoveHistoryDialog.static.title = 'Move history'; MoveHistoryDialog.static.size = 'small'; MoveHistoryDialog.static.actions = [ { modes: 'config', flags: ['safe', 'close'] }, { action: 'search', label: 'Search', modes: 'config', flags: ['primary', 'progressive'], disabled: true }, { action: 'switchNs', modes: 'config', label: 'Subject ↔ Talk' }, { action: 'goBack', modes: ['paused', 'pausedFound', 'found', 'notFound'], flags: ['safe', 'back'] }, { action: 'continue', label: 'Continue', modes: ['paused', 'pausedFound'], flags: ['primary', 'progressive'] }, { action: 'pause', label: 'Pause', modes: 'searching', flags: ['primary', 'destructive'] }, { action: 'copy', modes: ['pausedFound', 'found'], label: 'Copy results as wikitext' } ]; MoveHistoryDialog.prototype.initialize = function () { MoveHistoryDialog.parent.prototype.initialize.apply(this, arguments); let rt = mw.Title.newFromText(mw.config.get('wgRelevantPageName')); this.pageInput = new mw.widgets.TitleInputWidget({ $overlay: this.$overlay, api: api, excludeDynamicNamespaces: true, required: true, showMissing: false, value: rt && rt.namespace >= 0 ? rt.toText() : '' }).connect(this, { change: 'updateButton' }); this.directionInput = new OO.ui.RadioSelectInputWidget({ options: [ { data: 'newer', label: 'Oldest first' }, { data: 'older', label: 'Newest first' } ] }); this.sinceInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD' }).connect(this, { flag: 'updateButton' }); this.untilInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD', mustBeAfter: '2005-06-24' }).on('change', () => { let m = this.untilInput.getMoment(); this.sinceInput.mustBeBefore = m.isValid() ? m.add(1, 'days') : undefined; this.sinceInput.setValidityFlag(); }).connect(this, { flag: 'updateButton' }); this.form = new OO.ui.FormLayout({ items: [ new OO.ui.FieldLayout(this.pageInput, { label: 'Page:', align: 'top' }), this.switchButton, new OO.ui.FieldLayout(this.directionInput, { label: 'Direction:', align: 'top' }), new OO.ui.FieldLayout(this.sinceInput, { label: 'Since:', align: 'top' }), new OO.ui.FieldLayout(this.untilInput, { label: 'Until:', align: 'top' }) ] }).connect(this, { submit: ['executeAction', 'search'] }); this.$results = $('<div>'); this.form.$element .append($('<input>').attr({ type: 'submit', hidden: '' })) .appendTo(this.$body); }; MoveHistoryDialog.prototype.updateButton = function () { this.actions.setAbilities({ search: this.canSearch() }); }; MoveHistoryDialog.prototype.canSearch = function () { return !!this.pageInput.getValue() && !this.sinceInput.hasFlag('invalid') && !this.untilInput.hasFlag('invalid'); }; MoveHistoryDialog.prototype.getSetupProcess = function (data) { return MoveHistoryDialog.super.prototype.getSetupProcess.call(this, data).next(function () { this.updateButton(); this.actions.setMode('config'); }, this); }; MoveHistoryDialog.prototype.getReadyProcess = function (data) { return MoveHistoryDialog.super.prototype.getReadyProcess.call(this, data).next(function () { this.pageInput.focus(); }, this); }; MoveHistoryDialog.prototype.getActionProcess = function (action) { if (action === 'search') { if (!this.canSearch()) return; let config = [ this.pageInput.getMWTitle().toText(), this.directionInput.getValue(), this.sinceInput.getValue(), this.untilInput.getValue() ]; if (!this.config || config.some((v, i) => v !== this.config[i])) { this.config = config; this.search = new MoveHistorySearch(...config); } else { this.actions.setMode(this.search.mode); } this.form.toggle(false).$element.after(this.$results); this.setSize(this.search.moves.length ? 'large' : 'medium'); } else if (action === 'switchNs') { let t = this.pageInput.getMWTitle(); let ot = t && t[t.isTalkPage() ? 'getSubjectPage' : 'getTalkPage'](); if (ot) { this.pageInput.setValue(ot.toText()); } } else if (action === 'continue') { this.search.start(); } else if (action === 'pause') { this.search.paused = true; } else if (action === 'copy') { this.search.copyResults(); } else { this.actions.setMode('config'); this.$results.detach(); this.form.toggle(true); this.setSize('small'); } return MoveHistoryDialog.super.prototype.getActionProcess.call(this, action); }; dialog = new MoveHistoryDialog(); window.moveHistoryDialog = dialog; let winMan = new OO.ui.WindowManager(); winMan.addWindows([dialog]); winMan.$element.appendTo(OO.ui.getTeleportTarget()); dialog.open(); }); rrh07fxp6f2kwp8uk00uc8qd8wl2kz1 760334 760333 2026-09-04T21:55:15Z Nardog 40946 760334 javascript text/javascript window.moveHistoryDialog?.close(); $('#mw-notification-area').empty(); $('head > style').filter(function () { return this.textContent.includes('.movehistory'); }).remove(); mw.loader.using([ 'mediawiki.api', 'mediawiki.util', 'mediawiki.Title', 'mediawiki.DateFormatter', 'oojs-ui-windows', 'oojs-ui-widgets', 'mediawiki.widgets', 'mediawiki.widgets.DateInputWidget', 'oojs-ui.styles.icons-interactions', 'mediawiki.interface.helpers.styles' ], require => { mw.loader.addStyleTag(`.movehistory .oo-ui-window-body { padding: 1em; display: flex; justify-content: center; } .movehistory form { flex-grow: 1; } .movehistory .wikitable { width: 100%; margin-top: 0; word-break: break-word; } .movehistory-date, .movehistory-user { white-space: nowrap; } .movehistory-title { text-align: center; } .movehistory-comment { vertical-align: top; max-width: 16em; }`); let dialog; let DateFormatter = require('mediawiki.DateFormatter'); let wrapDate = (s, isWiki) => { let strs; let formatter = isWiki ? DateFormatter.forSiteZone() : DateFormatter; let date = new Date(s); let dateTime = formatter.formatTimeAndDate(date); let time = formatter.formatTime(date); if (dateTime.startsWith(time)) { time += dateTime.slice(time.length).replace(/^(\p{P}*).*/u, '$1'); strs = [time, dateTime.slice(time.length).trimStart()]; } else if (dateTime.endsWith(time)) { strs = [dateTime.slice(0, -time.length).trimEnd(), time]; } else { return [dateTime]; } strs = strs.map(str => isWiki ? nowiki(str) : document.createTextNode(str)); strs.splice(1, 0, '<br>'); return strs; }; let nowiki = s => s.replace( /["&'<=>\[\]{|}]|:(?=\/\/)|_(?=_)|~(?=~~)/g, m => '&#' + m.codePointAt(0) + ';' ); let articlePath = mw.config.get('wgArticlePath').replace(/\$1.*/, ''); let getPath = (pathname, search, hash) => { let s = ''; if (pathname?.startsWith(articlePath)) { s = decodeURIComponent(pathname.slice(articlePath.length)); } else if (search) { let title = mw.util.getParamValue('title', search); if (title) { s = title; } } if (hash) { s += mw.util.percentDecodeFragment(hash); } return s.replace(/_/g, ' '); }; let api = new mw.Api({ // userAgent: 'MoveHistory (https://en.wikipedia.org/wiki/User:Nardog/MoveHistory)' }); let arrow = document.dir === 'rtl' ? ' ← ' : ' → '; class MoveHistorySearch { constructor(page, dir, since, until) { this.$status = $('<div>').appendTo(dialog.$results.empty()); this.page = page; this.ascending = dir === 'newer'; let sinceTs = (since || '2005-06-25') + 'T00:00:00Z'; let untilTs; if (until) { untilTs = until + 'T23:59:59Z'; } this.params = { action: 'query', titles: page, prop: 'revisions', rvstart: this.ascending ? sinceTs : untilTs, rvend: this.ascending ? untilTs : sinceTs, rvdir: dir, rvprop: 'sha1|timestamp|user|comment', rvlimit: 'max', formatversion: 2 }; this.revCount = 0; this.candidates = []; this.titles = {}; this.noRedirLinks = new WeakSet(); this.moves = []; this.start(); } start() { this.setBusy(true); dialog.actions.setMode('searching'); this.i = 0; this.paused = false; this.doNext(); } doNext() { if (!this.paused && this.candidates.length) { this.loadMoves(); } else if (!this.paused && !this.complete && this.i < 4) { this.loadRevs(); } else { this.finish(); } } async loadRevs() { this.i++; this.setStatus(`Loading history${ this.revCount ? this.ascending ? ' after ' + this.lastDate : ' before ' + this.firstDate : '' }...`); try { let response = await api.get(this.params); let revs = response.query?.pages?.[0]?.revisions; if (revs) { this.processRevs(revs); } this.params.rvcontinue = response.continue?.rvcontinue; if (!this.params.rvcontinue) { this.complete = response.batchcomplete; } this.doNext(); } catch (error) { let errorMsg = error?.error?.info || error; this.finish(`Error loading revisions${errorMsg ? ': ' + errorMsg : ''}`); } } processRevs(revs) { this.revCount += revs.length; if (!this.ascending) { revs.reverse(); } revs.forEach(rev => { let comp = this.lastRev; this.lastRev = rev; if (!rev.comment || !rev.user || !rev.sha1 || !comp || comp.sha1 !== rev.sha1 ) { return; } let matches = rev.comment.match(/\[\[:?([^\]]+)\]\].+?\[\[:?([^\]]+)\]\]/); if (matches) { rev.matches = matches.slice(1); } this.candidates.push(rev); }); if (!this.ascending || !this.firstDate) { this.firstDate = DateFormatter.formatTimeAndDate(new Date(revs[0].timestamp)); } if (this.ascending || !this.lastDate) { this.lastDate = DateFormatter.formatTimeAndDate(new Date(this.lastRev.timestamp)); } } async loadMoves() { let rev = this.candidates[this.ascending ? 'shift' : 'pop'](); this.setStatus(`Seeing if there was a move at ${ DateFormatter.formatTimeAndDate(new Date(rev.timestamp)) }...`); let date = Date.parse(rev.timestamp) / 1000; try { (await api.get({ action: 'query', list: 'logevents', letype: 'move', lestart: date + 60, leend: date, leprop: 'details|title|user|parsedcomment', lelimit: 'max', formatversion: 2 })).query?.logevents?.reverse().some(le => { if (le.user !== rev.user || !rev.comment.includes(le.title)) return; let target = le?.params?.target_title; if (!target || !rev.comment.includes(target) || rev.matches && [le.title, target].some(s => !rev.matches.includes(s)) ) { return; } this.addMove({ date: rev.timestamp, offset: new Date(Date.parse(rev.timestamp) + 1000) .toISOString().slice(0, -5).replace(/\D/g, ''), from: le.title, to: target, user: le.user, comment: $.parseHTML(le.parsedcomment) }); return true; }); this.doNext(); } catch (error) { let errorMsg = error?.error?.info || error; this.finish(`Error loading moves${errorMsg ? ': ' + errorMsg : ''}`); } } addMove(move) { if (!this.moves.length) { this.lastName = this.ascending ? move.from : move.to; this.$trail = $('<p>').append(this.makeLink(this.lastName)); this.$tbody = $('<tbody>'); this.$table = $('<table>').addClass('wikitable').append( $('<thead>').append( $('<tr>').append( $('<th>').attr('rowspan', 2).text('Date'), $('<th>').text(this.ascending ? 'From' : 'To'), $('<th>').attr('rowspan', 2).text('Performer'), $('<th>').attr('rowspan', 2).text('Comment') ), $('<tr>').append( $('<th>').text(this.ascending ? 'To' : 'From') ) ), this.$tbody ); this.$status.after('<hr>', this.$trail, this.$table); } if (this.ascending) { if (this.lastName !== move.from) { this.$trail.append(arrow + '?' + arrow, this.makeLink(move.from)); } this.$trail.append(arrow, this.makeLink(move.to)); this.lastName = move.to; } else { if (this.lastName !== move.to) { this.$trail.prepend(this.makeLink(move.to), arrow + '?' + arrow); } this.$trail.prepend(this.makeLink(move.from), arrow); this.lastName = move.from; } this.$tbody.append( $('<tr>').append( $('<td>').attr({ class: 'movehistory-date', rowspan: 2 }).append( $('<a>').attr({ href: mw.util.getUrl(this.page, { action: 'history', offset: move.offset }), title: 'See history up to this move', target: '_blank' }).append(wrapDate(move.date)) ), $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.from : move.to) ), $('<td>').attr({ class: 'movehistory-user', rowspan: 2 }).append( this.makeLink('User:' + move.user, move.user, true), '<br>', $('<span>').addClass('mw-changeslist-links').append( $('<span>').append(this.makeLink('User talk:' + move.user, 'talk', true)), $('<span>').append(this.makeLink('Special:Contributions/' + move.user, 'contribs', true)) ) ), $('<td>').attr({ class: 'movehistory-comment', rowspan: 2 }).append( $(move.comment).clone().attr('target', '_blank') ) ), $('<tr>').append( $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.to : move.from) ) ) ); dialog.setSize('large'); this.moves.push(move); } finish(error) { let count = this.moves.length; let complete = this.complete && !this.candidates.length; this.setStatus(error || `Found ${count} move${count === 1 ? '' : 's'} in ${ this.revCount.toLocaleString('en') } revisions${ this.revCount ? ` from ${this.firstDate} to ${this.lastDate}` : '' }.${ complete ? '' : ' Click Continue to inspect more revisions.' }`); this.setBusy(); this.mode = complete ? count ? 'found' : 'notFound' : count ? 'pausedFound' : 'paused'; dialog.actions.setMode(this.mode); if (!count) return; this.queryTitles( Object.entries(this.titles) .filter(([, v]) => !v.processed).map(([k]) => k) ); } setBusy(busy) { MoveHistoryDialog.static.escapable = !busy; dialog.$navigation.toggleClass('oo-ui-pendingElement-pending', !!busy); } setStatus(text) { this.$status.text(text); dialog.updateSize(); console.log(text); } makeLink(title, text, allowRedirect) { let obj; if (Object.hasOwn(this.titles, title)) { obj = this.titles[title]; } else { obj = { links: [] }; this.titles[title] = obj; if (title === this.page) { obj.classes = ['mw-selflink', 'selflink']; obj.processed = true; } } let params = obj.red && { action: 'edit', redlink: 1 } || !allowRedirect && obj.redirect && { redirect: 'no' }; let $link = $('<a>').attr({ href: mw.util.getUrl(obj.canonical || title, params), title: obj.canonical || title, target: '_blank' }).addClass(obj.classes).text(text || title); if (!allowRedirect && !obj.processed) { this.noRedirLinks.add($link[0]); } if (!obj.processed) { obj.links.push($link[0]); } return $link; } queryTitles(titles) { if (!titles.length) return; let curTitles = titles.slice(0, 50); curTitles.forEach(title => { this.titles[title].processed = true; }); let { query } = api.post({ action: 'query', titles: curTitles, prop: 'info', inprop: 'linkclasses', inlinkcontext: this.page, formatversion: 2 }, { headers: { 'Promise-Non-Write-API-Action': 1 } }); query?.normalized?.forEach(entry => { if (!Object.hasOwn(this.titles, entry.from)) return; let obj = this.titles[entry.from]; obj.canonical = entry.to; this.titles[entry.to] = obj; }); query?.pages?.forEach(page => { if (!Object.hasOwn(this.titles, page.title)) return; let obj = this.titles[page.title]; let classes = page.linkclasses || []; if (page.missing && !page.known) { classes.push('new'); obj.red = true; } else if (classes.includes('mw-redirect')) { obj.redirect = true; } if (classes.length) { obj.classes = classes; } }); curTitles.forEach(title => { let obj = this.titles[title]; let $links = $(obj.links).addClass(obj.classes); $links.attr('href', i => mw.util.getUrl( obj.canonical || title, obj.red && { action: 'edit', redlink: 1 } || obj.redirect && this.noRedirLinks.has($links[i]) && { redirect: 'no' } )); if (obj.canonical) { $links.attr('title', obj.canonical); } delete obj.links; }); this.queryTitles(titles.slice(50)); } copyResults() { let text = this.$trail.contents().get().map(n => ( n.tagName === 'A' ? `[[:${n.textContent}]]` : n.textContent )).join('') + ` {| class="wikitable plainlinks" ! rowspan="2" | Date ! ${this.ascending ? 'From' : 'To'} ! rowspan="2" | Performer ! rowspan="2" | Comment |- ! ${this.ascending ? 'To' : 'From'} ${this.moves.map(move => `|- | rowspan="2" style="white-space: nowrap;" | [{{fullurl:${this.page}|action=history&offset=${move.offset}}} ${wrapDate(move.date, true).join('')}] | style="text-align: center;" | ${ this.titles[this.ascending ? move.from : move.to] && this.titles[this.ascending ? move.from : move.to].redirect ? `[{{fullurl:${this.ascending ? move.from : move.to}|redirect=no}} ${this.ascending ? move.from : move.to}]` : `[[:${this.ascending ? move.from : move.to}]]` } | rowspan="2" style="white-space: nowrap;" | [[User:${move.user}|${move.user}]]<br>([[User talk:${move.user}|talk]] &#124; [[Special:Contributions/${move.user}|contribs]]) | rowspan="2" ${ move.comment.length ? 'style="vertical-align: top;" | ' + move.comment.map(n => ( n.tagName === 'A' ? `[[:${ n.classList.contains('extiw') ? n.title : getPath(n.pathname, n.search, n.hash) }|${nowiki(n.textContent)}]]` : nowiki(n.textContent) )).join('') : '|' } |- | style="text-align: center;" | ${ this.titles[this.ascending ? move.to : move.from] && this.titles[this.ascending ? move.to : move.from].redirect ? `[{{fullurl:${this.ascending ? move.to : move.from}|redirect=no}} ${this.ascending ? move.to : move.from}]` : `[[:${this.ascending ? move.to : move.from}]]` } `).join('')}|}`; let $textarea = $('<textarea>').attr({ readonly: '', style: 'position:fixed;top:-100%' }).val(text).appendTo(document.body); $textarea[0].select(); let copied; try { copied = document.execCommand('copy'); } catch (e) {} $textarea.remove(); if (copied) { mw.notify('Copied'); } else { mw.notify('Copy failed', { type: 'error' }); } } } function MoveHistoryDialog(config) { MoveHistoryDialog.parent.call(this, config); this.$element.addClass('movehistory'); } OO.inheritClass(MoveHistoryDialog, OO.ui.ProcessDialog); MoveHistoryDialog.static.name = 'moveHistoryDialog'; MoveHistoryDialog.static.title = 'Move history'; MoveHistoryDialog.static.size = 'small'; MoveHistoryDialog.static.actions = [ { modes: 'config', flags: ['safe', 'close'] }, { action: 'search', label: 'Search', modes: 'config', flags: ['primary', 'progressive'], disabled: true }, { action: 'switchNs', modes: 'config', label: 'Subject ↔ Talk' }, { action: 'goBack', modes: ['paused', 'pausedFound', 'found', 'notFound'], flags: ['safe', 'back'] }, { action: 'continue', label: 'Continue', modes: ['paused', 'pausedFound'], flags: ['primary', 'progressive'] }, { action: 'pause', label: 'Pause', modes: 'searching', flags: ['primary', 'destructive'] }, { action: 'copy', modes: ['pausedFound', 'found'], label: 'Copy results as wikitext' } ]; MoveHistoryDialog.prototype.initialize = function () { MoveHistoryDialog.parent.prototype.initialize.apply(this, arguments); let rt = mw.Title.newFromText(mw.config.get('wgRelevantPageName')); this.pageInput = new mw.widgets.TitleInputWidget({ $overlay: this.$overlay, api: api, excludeDynamicNamespaces: true, required: true, showMissing: false, value: rt && rt.namespace >= 0 ? rt.toText() : '' }).connect(this, { change: 'updateButton' }); this.directionInput = new OO.ui.RadioSelectInputWidget({ options: [ { data: 'newer', label: 'Oldest first' }, { data: 'older', label: 'Newest first' } ] }); this.sinceInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD' }).connect(this, { flag: 'updateButton' }); this.untilInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD', mustBeAfter: '2005-06-24' }).on('change', () => { let m = this.untilInput.getMoment(); this.sinceInput.mustBeBefore = m.isValid() ? m.add(1, 'days') : undefined; this.sinceInput.setValidityFlag(); }).connect(this, { flag: 'updateButton' }); this.form = new OO.ui.FormLayout({ items: [ new OO.ui.FieldLayout(this.pageInput, { label: 'Page:', align: 'top' }), new OO.ui.FieldLayout(this.directionInput, { label: 'Direction:', align: 'top' }), new OO.ui.FieldLayout(this.sinceInput, { label: 'Since:', align: 'top' }), new OO.ui.FieldLayout(this.untilInput, { label: 'Until:', align: 'top' }) ] }).connect(this, { submit: ['executeAction', 'search'] }); this.$results = $('<div>'); this.form.$element .append($('<input>').attr({ type: 'submit', hidden: '' })) .appendTo(this.$body); }; MoveHistoryDialog.prototype.updateButton = function () { this.actions.setAbilities({ search: this.canSearch() }); }; MoveHistoryDialog.prototype.canSearch = function () { return !!this.pageInput.getValue() && !this.sinceInput.hasFlag('invalid') && !this.untilInput.hasFlag('invalid'); }; MoveHistoryDialog.prototype.getSetupProcess = function (data) { return MoveHistoryDialog.super.prototype.getSetupProcess.call(this, data).next(function () { this.updateButton(); this.actions.setMode('config'); }, this); }; MoveHistoryDialog.prototype.getReadyProcess = function (data) { return MoveHistoryDialog.super.prototype.getReadyProcess.call(this, data).next(function () { this.pageInput.focus(); }, this); }; MoveHistoryDialog.prototype.getActionProcess = function (action) { if (action === 'search') { if (!this.canSearch()) return; let config = [ this.pageInput.getMWTitle().toText(), this.directionInput.getValue(), this.sinceInput.getValue(), this.untilInput.getValue() ]; if (!this.config || config.some((v, i) => v !== this.config[i])) { this.config = config; this.search = new MoveHistorySearch(...config); } else { this.actions.setMode(this.search.mode); } this.form.toggle(false).$element.after(this.$results); this.setSize(this.search.moves.length ? 'large' : 'medium'); } else if (action === 'switchNs') { let t = this.pageInput.getMWTitle(); let ot = t && t[t.isTalkPage() ? 'getSubjectPage' : 'getTalkPage'](); if (ot) { this.pageInput.setValue(ot.toText()); } } else if (action === 'continue') { this.search.start(); } else if (action === 'pause') { this.search.paused = true; } else if (action === 'copy') { this.search.copyResults(); } else { this.actions.setMode('config'); this.$results.detach(); this.form.toggle(true); this.setSize('small'); } return MoveHistoryDialog.super.prototype.getActionProcess.call(this, action); }; dialog = new MoveHistoryDialog(); window.moveHistoryDialog = dialog; let winMan = new OO.ui.WindowManager(); winMan.addWindows([dialog]); winMan.$element.appendTo(OO.ui.getTeleportTarget()); dialog.open(); }); s631yfwli1rootn479vgsqqgfdw5zny 760335 760334 2026-09-04T21:55:56Z Nardog 40946 760335 javascript text/javascript window.moveHistoryDialog?.close(); $('#mw-notification-area').empty(); $('head > style').filter(function () { return this.textContent.includes('.movehistory'); }).remove(); mw.loader.using([ 'mediawiki.api', 'mediawiki.util', 'mediawiki.Title', 'mediawiki.DateFormatter', 'oojs-ui-windows', 'oojs-ui-widgets', 'mediawiki.widgets', 'mediawiki.widgets.DateInputWidget', 'oojs-ui.styles.icons-interactions', 'mediawiki.interface.helpers.styles' ], require => { mw.loader.addStyleTag(`.movehistory .oo-ui-window-body { padding: 1em; display: flex; justify-content: center; } .movehistory form { flex-grow: 1; } .movehistory .wikitable { width: 100%; margin-top: 0; word-break: break-word; } .movehistory-date, .movehistory-user { white-space: nowrap; } .movehistory-title { text-align: center; } .movehistory-comment { vertical-align: top; max-width: 16em; }`); let dialog; let DateFormatter = require('mediawiki.DateFormatter'); let wrapDate = (s, isWiki) => { let strs; let formatter = isWiki ? DateFormatter.forSiteZone() : DateFormatter; let date = new Date(s); let dateTime = formatter.formatTimeAndDate(date); let time = formatter.formatTime(date); if (dateTime.startsWith(time)) { time += dateTime.slice(time.length).replace(/^(\p{P}*).*/u, '$1'); strs = [time, dateTime.slice(time.length).trimStart()]; } else if (dateTime.endsWith(time)) { strs = [dateTime.slice(0, -time.length).trimEnd(), time]; } else { return [dateTime]; } strs = strs.map(str => isWiki ? nowiki(str) : document.createTextNode(str)); strs.splice(1, 0, '<br>'); return strs; }; let nowiki = s => s.replace( /["&'<=>\[\]{|}]|:(?=\/\/)|_(?=_)|~(?=~~)/g, m => '&#' + m.codePointAt(0) + ';' ); let articlePath = mw.config.get('wgArticlePath').replace(/\$1.*/, ''); let getPath = (pathname, search, hash) => { let s = ''; if (pathname?.startsWith(articlePath)) { s = decodeURIComponent(pathname.slice(articlePath.length)); } else if (search) { let title = mw.util.getParamValue('title', search); if (title) { s = title; } } if (hash) { s += mw.util.percentDecodeFragment(hash); } return s.replace(/_/g, ' '); }; let api = new mw.Api({ // userAgent: 'MoveHistory (https://en.wikipedia.org/wiki/User:Nardog/MoveHistory)' }); let arrow = document.dir === 'rtl' ? ' ← ' : ' → '; class MoveHistorySearch { constructor(page, dir, since, until) { this.$status = $('<div>').appendTo(dialog.$results.empty()); this.page = page; this.ascending = dir === 'newer'; let sinceTs = (since || '2005-06-25') + 'T00:00:00Z'; let untilTs; if (until) { untilTs = until + 'T23:59:59Z'; } this.params = { action: 'query', titles: page, prop: 'revisions', rvstart: this.ascending ? sinceTs : untilTs, rvend: this.ascending ? untilTs : sinceTs, rvdir: dir, rvprop: 'sha1|timestamp|user|comment', rvlimit: 'max', formatversion: 2 }; this.revCount = 0; this.candidates = []; this.titles = {}; this.noRedirLinks = new WeakSet(); this.moves = []; this.start(); } start() { this.setBusy(true); dialog.actions.setMode('searching'); this.i = 0; this.paused = false; this.doNext(); } doNext() { if (!this.paused && this.candidates.length) { this.loadMoves(); } else if (!this.paused && !this.complete && this.i < 4) { this.loadRevs(); } else { this.finish(); } } async loadRevs() { this.i++; this.setStatus(`Loading history${ this.revCount ? this.ascending ? ' after ' + this.lastDate : ' before ' + this.firstDate : '' }...`); try { let response = await api.get(this.params); let revs = response.query?.pages?.[0]?.revisions; if (revs) { this.processRevs(revs); } this.params.rvcontinue = response.continue?.rvcontinue; if (!this.params.rvcontinue) { this.complete = response.batchcomplete; } this.doNext(); } catch (error) { let errorMsg = error?.error?.info || error; this.finish(`Error loading revisions${errorMsg ? ': ' + errorMsg : ''}`); } } processRevs(revs) { this.revCount += revs.length; if (!this.ascending) { revs.reverse(); } revs.forEach(rev => { let comp = this.lastRev; this.lastRev = rev; if (!rev.comment || !rev.user || !rev.sha1 || !comp || comp.sha1 !== rev.sha1 ) { return; } let matches = rev.comment.match(/\[\[:?([^\]]+)\]\].+?\[\[:?([^\]]+)\]\]/); if (matches) { rev.matches = matches.slice(1); } this.candidates.push(rev); }); if (!this.ascending || !this.firstDate) { this.firstDate = DateFormatter.formatTimeAndDate(new Date(revs[0].timestamp)); } if (this.ascending || !this.lastDate) { this.lastDate = DateFormatter.formatTimeAndDate(new Date(this.lastRev.timestamp)); } } async loadMoves() { let rev = this.candidates[this.ascending ? 'shift' : 'pop'](); this.setStatus(`Seeing if there was a move at ${ DateFormatter.formatTimeAndDate(new Date(rev.timestamp)) }...`); let date = Date.parse(rev.timestamp) / 1000; try { (await api.get({ action: 'query', list: 'logevents', letype: 'move', lestart: date + 60, leend: date, leprop: 'details|title|user|parsedcomment', lelimit: 'max', formatversion: 2 })).query?.logevents?.reverse().some(le => { if (le.user !== rev.user || !rev.comment.includes(le.title)) return; let target = le?.params?.target_title; if (!target || !rev.comment.includes(target) || rev.matches && [le.title, target].some(s => !rev.matches.includes(s)) ) { return; } this.addMove({ date: rev.timestamp, offset: new Date(Date.parse(rev.timestamp) + 1000) .toISOString().slice(0, -5).replace(/\D/g, ''), from: le.title, to: target, user: le.user, comment: $.parseHTML(le.parsedcomment) }); return true; }); this.doNext(); } catch (error) { let errorMsg = error?.error?.info || error; this.finish(`Error loading moves${errorMsg ? ': ' + errorMsg : ''}`); } } addMove(move) { if (!this.moves.length) { this.lastName = this.ascending ? move.from : move.to; this.$trail = $('<p>').append(this.makeLink(this.lastName)); this.$tbody = $('<tbody>'); this.$table = $('<table>').addClass('wikitable').append( $('<thead>').append( $('<tr>').append( $('<th>').attr('rowspan', 2).text('Date'), $('<th>').text(this.ascending ? 'From' : 'To'), $('<th>').attr('rowspan', 2).text('Performer'), $('<th>').attr('rowspan', 2).text('Comment') ), $('<tr>').append( $('<th>').text(this.ascending ? 'To' : 'From') ) ), this.$tbody ); this.$status.after('<hr>', this.$trail, this.$table); } if (this.ascending) { if (this.lastName !== move.from) { this.$trail.append(arrow + '?' + arrow, this.makeLink(move.from)); } this.$trail.append(arrow, this.makeLink(move.to)); this.lastName = move.to; } else { if (this.lastName !== move.to) { this.$trail.prepend(this.makeLink(move.to), arrow + '?' + arrow); } this.$trail.prepend(this.makeLink(move.from), arrow); this.lastName = move.from; } this.$tbody.append( $('<tr>').append( $('<td>').attr({ class: 'movehistory-date', rowspan: 2 }).append( $('<a>').attr({ href: mw.util.getUrl(this.page, { action: 'history', offset: move.offset }), title: 'See history up to this move', target: '_blank' }).append(wrapDate(move.date)) ), $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.from : move.to) ), $('<td>').attr({ class: 'movehistory-user', rowspan: 2 }).append( this.makeLink('User:' + move.user, move.user, true), '<br>', $('<span>').addClass('mw-changeslist-links').append( $('<span>').append(this.makeLink('User talk:' + move.user, 'talk', true)), $('<span>').append(this.makeLink('Special:Contributions/' + move.user, 'contribs', true)) ) ), $('<td>').attr({ class: 'movehistory-comment', rowspan: 2 }).append( $(move.comment).clone().attr('target', '_blank') ) ), $('<tr>').append( $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.to : move.from) ) ) ); dialog.setSize('large'); this.moves.push(move); } finish(error) { let count = this.moves.length; let complete = this.complete && !this.candidates.length; this.setStatus(error || `Found ${count} move${count === 1 ? '' : 's'} in ${ this.revCount.toLocaleString('en') } revisions${ this.revCount ? ` from ${this.firstDate} to ${this.lastDate}` : '' }.${ complete ? '' : ' Click Continue to inspect more revisions.' }`); this.setBusy(); this.mode = complete ? count ? 'found' : 'notFound' : count ? 'pausedFound' : 'paused'; dialog.actions.setMode(this.mode); if (!count) return; this.queryTitles( Object.entries(this.titles) .filter(([, v]) => !v.processed).map(([k]) => k) ); } setBusy(busy) { MoveHistoryDialog.static.escapable = !busy; dialog.$navigation.toggleClass('oo-ui-pendingElement-pending', !!busy); } setStatus(text) { this.$status.text(text); dialog.updateSize(); console.log(text); } makeLink(title, text, allowRedirect) { let obj; if (Object.hasOwn(this.titles, title)) { obj = this.titles[title]; } else { obj = { links: [] }; this.titles[title] = obj; if (title === this.page) { obj.classes = ['mw-selflink', 'selflink']; obj.processed = true; } } let params = obj.red && { action: 'edit', redlink: 1 } || !allowRedirect && obj.redirect && { redirect: 'no' }; let $link = $('<a>').attr({ href: mw.util.getUrl(obj.canonical || title, params), title: obj.canonical || title, target: '_blank' }).addClass(obj.classes).text(text || title); if (!allowRedirect && !obj.processed) { this.noRedirLinks.add($link[0]); } if (!obj.processed) { obj.links.push($link[0]); } return $link; } queryTitles(titles) { if (!titles.length) return; let curTitles = titles.slice(0, 50); curTitles.forEach(title => { this.titles[title].processed = true; }); let { query } = api.post({ action: 'query', titles: curTitles, prop: 'info', inprop: 'linkclasses', inlinkcontext: this.page, formatversion: 2 }, { headers: { 'Promise-Non-Write-API-Action': 1 } }); query?.normalized?.forEach(entry => { if (!Object.hasOwn(this.titles, entry.from)) return; let obj = this.titles[entry.from]; obj.canonical = entry.to; this.titles[entry.to] = obj; }); query?.pages?.forEach(page => { if (!Object.hasOwn(this.titles, page.title)) return; let obj = this.titles[page.title]; let classes = page.linkclasses || []; if (page.missing && !page.known) { classes.push('new'); obj.red = true; } else if (classes.includes('mw-redirect')) { obj.redirect = true; } if (classes.length) { obj.classes = classes; } }); curTitles.forEach(title => { let obj = this.titles[title]; let $links = $(obj.links).addClass(obj.classes); $links.attr('href', i => mw.util.getUrl( obj.canonical || title, obj.red && { action: 'edit', redlink: 1 } || obj.redirect && this.noRedirLinks.has($links[i]) && { redirect: 'no' } )); if (obj.canonical) { $links.attr('title', obj.canonical); } delete obj.links; }); this.queryTitles(titles.slice(50)); } copyResults() { let text = this.$trail.contents().get().map(n => ( n.tagName === 'A' ? `[[:${n.textContent}]]` : n.textContent )).join('') + ` {| class="wikitable plainlinks" ! rowspan="2" | Date ! ${this.ascending ? 'From' : 'To'} ! rowspan="2" | Performer ! rowspan="2" | Comment |- ! ${this.ascending ? 'To' : 'From'} ${this.moves.map(move => `|- | rowspan="2" style="white-space: nowrap;" | [{{fullurl:${this.page}|action=history&offset=${move.offset}}} ${wrapDate(move.date, true).join('')}] | style="text-align: center;" | ${ this.titles[this.ascending ? move.from : move.to] && this.titles[this.ascending ? move.from : move.to].redirect ? `[{{fullurl:${this.ascending ? move.from : move.to}|redirect=no}} ${this.ascending ? move.from : move.to}]` : `[[:${this.ascending ? move.from : move.to}]]` } | rowspan="2" style="white-space: nowrap;" | [[User:${move.user}|${move.user}]]<br>([[User talk:${move.user}|talk]] &#124; [[Special:Contributions/${move.user}|contribs]]) | rowspan="2" ${ move.comment.length ? 'style="vertical-align: top;" | ' + move.comment.map(n => ( n.tagName === 'A' ? `[[:${ n.classList.contains('extiw') ? n.title : getPath(n.pathname, n.search, n.hash) }|${nowiki(n.textContent)}]]` : nowiki(n.textContent) )).join('') : '|' } |- | style="text-align: center;" | ${ this.titles[this.ascending ? move.to : move.from] && this.titles[this.ascending ? move.to : move.from].redirect ? `[{{fullurl:${this.ascending ? move.to : move.from}|redirect=no}} ${this.ascending ? move.to : move.from}]` : `[[:${this.ascending ? move.to : move.from}]]` } `).join('')}|}`; let $textarea = $('<textarea>').attr({ readonly: '', style: 'position:fixed;top:-100%' }).val(text).appendTo(document.body); $textarea[0].select(); let copied; try { copied = document.execCommand('copy'); } catch (e) {} $textarea.remove(); if (copied) { mw.notify('Copied'); } else { mw.notify('Copy failed', { type: 'error' }); } } } function MoveHistoryDialog(config) { MoveHistoryDialog.parent.call(this, config); this.$element.addClass('movehistory'); } OO.inheritClass(MoveHistoryDialog, OO.ui.ProcessDialog); MoveHistoryDialog.static.name = 'moveHistoryDialog'; MoveHistoryDialog.static.title = 'Move history'; MoveHistoryDialog.static.size = 'small'; MoveHistoryDialog.static.actions = [ { modes: 'config', flags: ['safe', 'close'] }, { action: 'search', label: 'Search', modes: 'config', flags: ['primary', 'progressive'], disabled: true }, { action: 'goBack', modes: ['paused', 'pausedFound', 'found', 'notFound'], flags: ['safe', 'back'] }, { action: 'continue', label: 'Continue', modes: ['paused', 'pausedFound'], flags: ['primary', 'progressive'] }, { action: 'pause', label: 'Pause', modes: 'searching', flags: ['primary', 'destructive'] }, { action: 'copy', modes: ['pausedFound', 'found'], label: 'Copy results as wikitext' } ]; MoveHistoryDialog.prototype.initialize = function () { MoveHistoryDialog.parent.prototype.initialize.apply(this, arguments); let rt = mw.Title.newFromText(mw.config.get('wgRelevantPageName')); let defaultValue = rt && rt.namespace >= 0 ? rt.toText() : ''; this.pageInput = new mw.widgets.TitleInputWidget({ $overlay: this.$overlay, api: api, excludeDynamicNamespaces: true, required: true, showMissing: false, value: defaultValue }).connect(this, { change: 'updateButton' }).on('change', OO.ui.debounce(value => { this.switchButton.setDisabled(!value || !this.pageInput.getMWTitle()); }, 250)); this.switchButton = new OO.ui.ButtonWidget({ disabled: !defaultValue, label: 'Subject ↔ Talk' }).on('click', () => { let t = this.pageInput.getMWTitle(); let ot = t && t[t.isTalkPage() ? 'getSubjectPage' : 'getTalkPage'](); if (!ot) return; this.pageInput.setValue(ot.toText()); }); this.directionInput = new OO.ui.RadioSelectInputWidget({ options: [ { data: 'newer', label: 'Oldest first' }, { data: 'older', label: 'Newest first' } ] }); this.sinceInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD' }).connect(this, { flag: 'updateButton' }); this.untilInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD', mustBeAfter: '2005-06-24' }).on('change', () => { let m = this.untilInput.getMoment(); this.sinceInput.mustBeBefore = m.isValid() ? m.add(1, 'days') : undefined; this.sinceInput.setValidityFlag(); }).connect(this, { flag: 'updateButton' }); this.form = new OO.ui.FormLayout({ items: [ new OO.ui.FieldLayout(this.pageInput, { label: 'Page:', align: 'top' }), this.switchButton, new OO.ui.FieldLayout(this.directionInput, { label: 'Direction:', align: 'top' }), new OO.ui.FieldLayout(this.sinceInput, { label: 'Since:', align: 'top' }), new OO.ui.FieldLayout(this.untilInput, { label: 'Until:', align: 'top' }) ] }).connect(this, { submit: ['executeAction', 'search'] }); this.$results = $('<div>'); this.form.$element .append($('<input>').attr({ type: 'submit', hidden: '' })) .appendTo(this.$body); }; MoveHistoryDialog.prototype.updateButton = function () { this.actions.setAbilities({ search: this.canSearch() }); }; MoveHistoryDialog.prototype.canSearch = function () { return !!this.pageInput.getValue() && !this.sinceInput.hasFlag('invalid') && !this.untilInput.hasFlag('invalid'); }; MoveHistoryDialog.prototype.getSetupProcess = function (data) { return MoveHistoryDialog.super.prototype.getSetupProcess.call(this, data).next(function () { this.updateButton(); this.actions.setMode('config'); }, this); }; MoveHistoryDialog.prototype.getReadyProcess = function (data) { return MoveHistoryDialog.super.prototype.getReadyProcess.call(this, data).next(function () { this.pageInput.focus(); }, this); }; MoveHistoryDialog.prototype.getActionProcess = function (action) { if (action === 'search') { if (!this.canSearch()) return; let config = [ this.pageInput.getMWTitle().toText(), this.directionInput.getValue(), this.sinceInput.getValue(), this.untilInput.getValue() ]; if (!this.config || config.some((v, i) => v !== this.config[i])) { this.config = config; this.search = new MoveHistorySearch(...config); } else { this.actions.setMode(this.search.mode); } this.form.toggle(false).$element.after(this.$results); this.setSize(this.search.moves.length ? 'large' : 'medium'); } else if (action === 'continue') { this.search.start(); } else if (action === 'pause') { this.search.paused = true; } else if (action === 'copy') { this.search.copyResults(); } else { this.actions.setMode('config'); this.$results.detach(); this.form.toggle(true); this.setSize('small'); } return MoveHistoryDialog.super.prototype.getActionProcess.call(this, action); }; dialog = new MoveHistoryDialog(); window.moveHistoryDialog = dialog; let winMan = new OO.ui.WindowManager(); winMan.addWindows([dialog]); winMan.$element.appendTo(OO.ui.getTeleportTarget()); dialog.open(); }); aaiey76r7hdbepb3mgjpqymqnk1npxv 760336 760335 2026-09-04T21:56:07Z Nardog 40946 760336 javascript text/javascript window.moveHistoryDialog?.close(); $('#mw-notification-area').empty(); $('head > style').filter(function () { return this.textContent.includes('.movehistory'); }).remove(); mw.loader.using([ 'mediawiki.api', 'mediawiki.util', 'mediawiki.Title', 'mediawiki.DateFormatter', 'oojs-ui-windows', 'oojs-ui-widgets', 'mediawiki.widgets', 'mediawiki.widgets.DateInputWidget', 'oojs-ui.styles.icons-interactions', 'mediawiki.interface.helpers.styles' ], require => { mw.loader.addStyleTag(`.movehistory .oo-ui-window-body { padding: 1em; display: flex; justify-content: center; } .movehistory form { flex-grow: 1; } .movehistory .wikitable { width: 100%; margin-top: 0; word-break: break-word; } .movehistory-date, .movehistory-user { white-space: nowrap; } .movehistory-title { text-align: center; } .movehistory-comment { vertical-align: top; max-width: 16em; }`); let dialog; let DateFormatter = require('mediawiki.DateFormatter'); let wrapDate = (s, isWiki) => { let strs; let formatter = isWiki ? DateFormatter.forSiteZone() : DateFormatter; let date = new Date(s); let dateTime = formatter.formatTimeAndDate(date); let time = formatter.formatTime(date); if (dateTime.startsWith(time)) { time += dateTime.slice(time.length).replace(/^(\p{P}*).*/u, '$1'); strs = [time, dateTime.slice(time.length).trimStart()]; } else if (dateTime.endsWith(time)) { strs = [dateTime.slice(0, -time.length).trimEnd(), time]; } else { return [dateTime]; } strs = strs.map(str => isWiki ? nowiki(str) : document.createTextNode(str)); strs.splice(1, 0, '<br>'); return strs; }; let nowiki = s => s.replace( /["&'<=>\[\]{|}]|:(?=\/\/)|_(?=_)|~(?=~~)/g, m => '&#' + m.codePointAt(0) + ';' ); let articlePath = mw.config.get('wgArticlePath').replace(/\$1.*/, ''); let getPath = (pathname, search, hash) => { let s = ''; if (pathname?.startsWith(articlePath)) { s = decodeURIComponent(pathname.slice(articlePath.length)); } else if (search) { let title = mw.util.getParamValue('title', search); if (title) { s = title; } } if (hash) { s += mw.util.percentDecodeFragment(hash); } return s.replace(/_/g, ' '); }; let api = new mw.Api({ // userAgent: 'MoveHistory (https://en.wikipedia.org/wiki/User:Nardog/MoveHistory)' }); let arrow = document.dir === 'rtl' ? ' ← ' : ' → '; class MoveHistorySearch { constructor(page, dir, since, until) { this.$status = $('<div>').appendTo(dialog.$results.empty()); this.page = page; this.ascending = dir === 'newer'; let sinceTs = (since || '2005-06-25') + 'T00:00:00Z'; let untilTs; if (until) { untilTs = until + 'T23:59:59Z'; } this.params = { action: 'query', titles: page, prop: 'revisions', rvstart: this.ascending ? sinceTs : untilTs, rvend: this.ascending ? untilTs : sinceTs, rvdir: dir, rvprop: 'sha1|timestamp|user|comment', rvlimit: 'max', formatversion: 2 }; this.revCount = 0; this.candidates = []; this.titles = {}; this.noRedirLinks = new WeakSet(); this.moves = []; this.start(); } start() { this.setBusy(true); dialog.actions.setMode('searching'); this.i = 0; this.paused = false; this.doNext(); } doNext() { if (!this.paused && this.candidates.length) { this.loadMoves(); } else if (!this.paused && !this.complete && this.i < 4) { this.loadRevs(); } else { this.finish(); } } async loadRevs() { this.i++; this.setStatus(`Loading history${ this.revCount ? this.ascending ? ' after ' + this.lastDate : ' before ' + this.firstDate : '' }...`); try { let response = await api.get(this.params); let revs = response.query?.pages?.[0]?.revisions; if (revs) { this.processRevs(revs); } this.params.rvcontinue = response.continue?.rvcontinue; if (!this.params.rvcontinue) { this.complete = response.batchcomplete; } this.doNext(); } catch (error) { let errorMsg = error?.error?.info || error; this.finish(`Error loading revisions${errorMsg ? ': ' + errorMsg : ''}`); } } processRevs(revs) { this.revCount += revs.length; if (!this.ascending) { revs.reverse(); } revs.forEach(rev => { let comp = this.lastRev; this.lastRev = rev; if (!rev.comment || !rev.user || !rev.sha1 || !comp || comp.sha1 !== rev.sha1 ) { return; } let matches = rev.comment.match(/\[\[:?([^\]]+)\]\].+?\[\[:?([^\]]+)\]\]/); if (matches) { rev.matches = matches.slice(1); } this.candidates.push(rev); }); if (!this.ascending || !this.firstDate) { this.firstDate = DateFormatter.formatTimeAndDate(new Date(revs[0].timestamp)); } if (this.ascending || !this.lastDate) { this.lastDate = DateFormatter.formatTimeAndDate(new Date(this.lastRev.timestamp)); } } async loadMoves() { let rev = this.candidates[this.ascending ? 'shift' : 'pop'](); this.setStatus(`Seeing if there was a move at ${ DateFormatter.formatTimeAndDate(new Date(rev.timestamp)) }...`); let date = Date.parse(rev.timestamp) / 1000; try { (await api.get({ action: 'query', list: 'logevents', letype: 'move', lestart: date + 60, leend: date, leprop: 'details|title|user|parsedcomment', lelimit: 'max', formatversion: 2 })).query?.logevents?.reverse().some(le => { if (le.user !== rev.user || !rev.comment.includes(le.title)) return; let target = le?.params?.target_title; if (!target || !rev.comment.includes(target) || rev.matches && [le.title, target].some(s => !rev.matches.includes(s)) ) { return; } this.addMove({ date: rev.timestamp, offset: new Date(Date.parse(rev.timestamp) + 1000) .toISOString().slice(0, -5).replace(/\D/g, ''), from: le.title, to: target, user: le.user, comment: $.parseHTML(le.parsedcomment) }); return true; }); this.doNext(); } catch (error) { let errorMsg = error?.error?.info || error; this.finish(`Error loading moves${errorMsg ? ': ' + errorMsg : ''}`); } } addMove(move) { if (!this.moves.length) { this.lastName = this.ascending ? move.from : move.to; this.$trail = $('<p>').append(this.makeLink(this.lastName)); this.$tbody = $('<tbody>'); this.$table = $('<table>').addClass('wikitable').append( $('<thead>').append( $('<tr>').append( $('<th>').attr('rowspan', 2).text('Date'), $('<th>').text(this.ascending ? 'From' : 'To'), $('<th>').attr('rowspan', 2).text('Performer'), $('<th>').attr('rowspan', 2).text('Comment') ), $('<tr>').append( $('<th>').text(this.ascending ? 'To' : 'From') ) ), this.$tbody ); this.$status.after('<hr>', this.$trail, this.$table); } if (this.ascending) { if (this.lastName !== move.from) { this.$trail.append(arrow + '?' + arrow, this.makeLink(move.from)); } this.$trail.append(arrow, this.makeLink(move.to)); this.lastName = move.to; } else { if (this.lastName !== move.to) { this.$trail.prepend(this.makeLink(move.to), arrow + '?' + arrow); } this.$trail.prepend(this.makeLink(move.from), arrow); this.lastName = move.from; } this.$tbody.append( $('<tr>').append( $('<td>').attr({ class: 'movehistory-date', rowspan: 2 }).append( $('<a>').attr({ href: mw.util.getUrl(this.page, { action: 'history', offset: move.offset }), title: 'See history up to this move', target: '_blank' }).append(wrapDate(move.date)) ), $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.from : move.to) ), $('<td>').attr({ class: 'movehistory-user', rowspan: 2 }).append( this.makeLink('User:' + move.user, move.user, true), '<br>', $('<span>').addClass('mw-changeslist-links').append( $('<span>').append(this.makeLink('User talk:' + move.user, 'talk', true)), $('<span>').append(this.makeLink('Special:Contributions/' + move.user, 'contribs', true)) ) ), $('<td>').attr({ class: 'movehistory-comment', rowspan: 2 }).append( $(move.comment).clone().attr('target', '_blank') ) ), $('<tr>').append( $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.to : move.from) ) ) ); dialog.setSize('large'); this.moves.push(move); } finish(error) { let count = this.moves.length; let complete = this.complete && !this.candidates.length; this.setStatus(error || `Found ${count} move${count === 1 ? '' : 's'} in ${ this.revCount.toLocaleString('en') } revisions${ this.revCount ? ` from ${this.firstDate} to ${this.lastDate}` : '' }.${ complete ? '' : ' Click Continue to inspect more revisions.' }`); this.setBusy(); this.mode = complete ? count ? 'found' : 'notFound' : count ? 'pausedFound' : 'paused'; dialog.actions.setMode(this.mode); if (!count) return; this.queryTitles( Object.entries(this.titles) .filter(([, v]) => !v.processed).map(([k]) => k) ); } setBusy(busy) { MoveHistoryDialog.static.escapable = !busy; dialog.$navigation.toggleClass('oo-ui-pendingElement-pending', !!busy); } setStatus(text) { this.$status.text(text); dialog.updateSize(); console.log(text); } makeLink(title, text, allowRedirect) { let obj; if (Object.hasOwn(this.titles, title)) { obj = this.titles[title]; } else { obj = { links: [] }; this.titles[title] = obj; if (title === this.page) { obj.classes = ['mw-selflink', 'selflink']; obj.processed = true; } } let params = obj.red && { action: 'edit', redlink: 1 } || !allowRedirect && obj.redirect && { redirect: 'no' }; let $link = $('<a>').attr({ href: mw.util.getUrl(obj.canonical || title, params), title: obj.canonical || title, target: '_blank' }).addClass(obj.classes).text(text || title); if (!allowRedirect && !obj.processed) { this.noRedirLinks.add($link[0]); } if (!obj.processed) { obj.links.push($link[0]); } return $link; } queryTitles(titles) { if (!titles.length) return; let curTitles = titles.slice(0, 50); curTitles.forEach(title => { this.titles[title].processed = true; }); let { query } = api.post({ action: 'query', titles: curTitles, prop: 'info', inprop: 'linkclasses', inlinkcontext: this.page, formatversion: 2 }, { headers: { 'Promise-Non-Write-API-Action': 1 } }); query?.normalized?.forEach(entry => { if (!Object.hasOwn(this.titles, entry.from)) return; let obj = this.titles[entry.from]; obj.canonical = entry.to; this.titles[entry.to] = obj; }); query?.pages?.forEach(page => { if (!Object.hasOwn(this.titles, page.title)) return; let obj = this.titles[page.title]; let classes = page.linkclasses || []; if (page.missing && !page.known) { classes.push('new'); obj.red = true; } else if (classes.includes('mw-redirect')) { obj.redirect = true; } if (classes.length) { obj.classes = classes; } }); curTitles.forEach(title => { let obj = this.titles[title]; let $links = $(obj.links).addClass(obj.classes); $links.attr('href', i => mw.util.getUrl( obj.canonical || title, obj.red && { action: 'edit', redlink: 1 } || obj.redirect && this.noRedirLinks.has($links[i]) && { redirect: 'no' } )); if (obj.canonical) { $links.attr('title', obj.canonical); } delete obj.links; }); this.queryTitles(titles.slice(50)); } copyResults() { let text = this.$trail.contents().get().map(n => ( n.tagName === 'A' ? `[[:${n.textContent}]]` : n.textContent )).join('') + ` {| class="wikitable plainlinks" ! rowspan="2" | Date ! ${this.ascending ? 'From' : 'To'} ! rowspan="2" | Performer ! rowspan="2" | Comment |- ! ${this.ascending ? 'To' : 'From'} ${this.moves.map(move => `|- | rowspan="2" style="white-space: nowrap;" | [{{fullurl:${this.page}|action=history&offset=${move.offset}}} ${wrapDate(move.date, true).join('')}] | style="text-align: center;" | ${ this.titles[this.ascending ? move.from : move.to] && this.titles[this.ascending ? move.from : move.to].redirect ? `[{{fullurl:${this.ascending ? move.from : move.to}|redirect=no}} ${this.ascending ? move.from : move.to}]` : `[[:${this.ascending ? move.from : move.to}]]` } | rowspan="2" style="white-space: nowrap;" | [[User:${move.user}|${move.user}]]<br>([[User talk:${move.user}|talk]] &#124; [[Special:Contributions/${move.user}|contribs]]) | rowspan="2" ${ move.comment.length ? 'style="vertical-align: top;" | ' + move.comment.map(n => ( n.tagName === 'A' ? `[[:${ n.classList.contains('extiw') ? n.title : getPath(n.pathname, n.search, n.hash) }|${nowiki(n.textContent)}]]` : nowiki(n.textContent) )).join('') : '|' } |- | style="text-align: center;" | ${ this.titles[this.ascending ? move.to : move.from] && this.titles[this.ascending ? move.to : move.from].redirect ? `[{{fullurl:${this.ascending ? move.to : move.from}|redirect=no}} ${this.ascending ? move.to : move.from}]` : `[[:${this.ascending ? move.to : move.from}]]` } `).join('')}|}`; let $textarea = $('<textarea>').attr({ readonly: '', style: 'position:fixed;top:-100%' }).val(text).appendTo(document.body); $textarea[0].select(); let copied; try { copied = document.execCommand('copy'); } catch (e) {} $textarea.remove(); if (copied) { mw.notify('Copied'); } else { mw.notify('Copy failed', { type: 'error' }); } } } function MoveHistoryDialog(config) { MoveHistoryDialog.parent.call(this, config); this.$element.addClass('movehistory'); } OO.inheritClass(MoveHistoryDialog, OO.ui.ProcessDialog); MoveHistoryDialog.static.name = 'moveHistoryDialog'; MoveHistoryDialog.static.title = 'Move history'; MoveHistoryDialog.static.size = 'small'; MoveHistoryDialog.static.actions = [ { modes: 'config', flags: ['safe', 'close'] }, { action: 'search', label: 'Search', modes: 'config', flags: ['primary', 'progressive'], disabled: true }, { action: 'goBack', modes: ['paused', 'pausedFound', 'found', 'notFound'], flags: ['safe', 'back'] }, { action: 'continue', label: 'Continue', modes: ['paused', 'pausedFound'], flags: ['primary', 'progressive'] }, { action: 'pause', label: 'Pause', modes: 'searching', flags: ['primary', 'destructive'] }, { action: 'copy', modes: ['pausedFound', 'found'], label: 'Copy results as wikitext' } ]; MoveHistoryDialog.prototype.initialize = function () { MoveHistoryDialog.parent.prototype.initialize.apply(this, arguments); let rt = mw.Title.newFromText(mw.config.get('wgRelevantPageName')); let defaultValue = rt && rt.namespace >= 0 ? rt.toText() : ''; this.pageInput = new mw.widgets.TitleInputWidget({ $overlay: this.$overlay, api: api, excludeDynamicNamespaces: true, required: true, showMissing: false, value: defaultValue }).connect(this, { change: 'updateButton' }).on('change', OO.ui.debounce(value => { this.switchButton.setDisabled(!value || !this.pageInput.getMWTitle()); }, 250)); this.switchButton = new OO.ui.ButtonWidget({ disabled: !defaultValue, label: 'Subject ↔ Talk' }).on('click', () => { let t = this.pageInput.getMWTitle(); let ot = t?.[t.isTalkPage() ? 'getSubjectPage' : 'getTalkPage'](); if (!ot) return; this.pageInput.setValue(ot.toText()); }); this.directionInput = new OO.ui.RadioSelectInputWidget({ options: [ { data: 'newer', label: 'Oldest first' }, { data: 'older', label: 'Newest first' } ] }); this.sinceInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD' }).connect(this, { flag: 'updateButton' }); this.untilInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD', mustBeAfter: '2005-06-24' }).on('change', () => { let m = this.untilInput.getMoment(); this.sinceInput.mustBeBefore = m.isValid() ? m.add(1, 'days') : undefined; this.sinceInput.setValidityFlag(); }).connect(this, { flag: 'updateButton' }); this.form = new OO.ui.FormLayout({ items: [ new OO.ui.FieldLayout(this.pageInput, { label: 'Page:', align: 'top' }), this.switchButton, new OO.ui.FieldLayout(this.directionInput, { label: 'Direction:', align: 'top' }), new OO.ui.FieldLayout(this.sinceInput, { label: 'Since:', align: 'top' }), new OO.ui.FieldLayout(this.untilInput, { label: 'Until:', align: 'top' }) ] }).connect(this, { submit: ['executeAction', 'search'] }); this.$results = $('<div>'); this.form.$element .append($('<input>').attr({ type: 'submit', hidden: '' })) .appendTo(this.$body); }; MoveHistoryDialog.prototype.updateButton = function () { this.actions.setAbilities({ search: this.canSearch() }); }; MoveHistoryDialog.prototype.canSearch = function () { return !!this.pageInput.getValue() && !this.sinceInput.hasFlag('invalid') && !this.untilInput.hasFlag('invalid'); }; MoveHistoryDialog.prototype.getSetupProcess = function (data) { return MoveHistoryDialog.super.prototype.getSetupProcess.call(this, data).next(function () { this.updateButton(); this.actions.setMode('config'); }, this); }; MoveHistoryDialog.prototype.getReadyProcess = function (data) { return MoveHistoryDialog.super.prototype.getReadyProcess.call(this, data).next(function () { this.pageInput.focus(); }, this); }; MoveHistoryDialog.prototype.getActionProcess = function (action) { if (action === 'search') { if (!this.canSearch()) return; let config = [ this.pageInput.getMWTitle().toText(), this.directionInput.getValue(), this.sinceInput.getValue(), this.untilInput.getValue() ]; if (!this.config || config.some((v, i) => v !== this.config[i])) { this.config = config; this.search = new MoveHistorySearch(...config); } else { this.actions.setMode(this.search.mode); } this.form.toggle(false).$element.after(this.$results); this.setSize(this.search.moves.length ? 'large' : 'medium'); } else if (action === 'continue') { this.search.start(); } else if (action === 'pause') { this.search.paused = true; } else if (action === 'copy') { this.search.copyResults(); } else { this.actions.setMode('config'); this.$results.detach(); this.form.toggle(true); this.setSize('small'); } return MoveHistoryDialog.super.prototype.getActionProcess.call(this, action); }; dialog = new MoveHistoryDialog(); window.moveHistoryDialog = dialog; let winMan = new OO.ui.WindowManager(); winMan.addWindows([dialog]); winMan.$element.appendTo(OO.ui.getTeleportTarget()); dialog.open(); }); e4q4t4vw5kzbrl6v3b7zji3ov0jjm96 760337 760336 2026-09-04T21:58:27Z Nardog 40946 760337 javascript text/javascript window.moveHistoryDialog?.close(); $('#mw-notification-area').empty(); $('head > style').filter(function () { return this.textContent.includes('.movehistory'); }).remove(); mw.loader.using([ 'mediawiki.api', 'mediawiki.util', 'mediawiki.Title', 'mediawiki.DateFormatter', 'oojs-ui-windows', 'oojs-ui-widgets', 'mediawiki.widgets', 'mediawiki.widgets.DateInputWidget', 'oojs-ui.styles.icons-interactions', 'mediawiki.interface.helpers.styles' ], require => { mw.loader.addStyleTag(`.movehistory .oo-ui-window-body { padding: 1em; display: flex; justify-content: center; } .movehistory form { flex-grow: 1; } .movehistory .wikitable { width: 100%; margin-top: 0; word-break: break-word; } .movehistory-date, .movehistory-user { white-space: nowrap; } .movehistory-title { text-align: center; } .movehistory-comment { vertical-align: top; max-width: 16em; }`); let dialog; let DateFormatter = require('mediawiki.DateFormatter'); let wrapDate = (s, isWiki) => { let strs; let formatter = isWiki ? DateFormatter.forSiteZone() : DateFormatter; let date = new Date(s); let dateTime = formatter.formatTimeAndDate(date); let time = formatter.formatTime(date); if (dateTime.startsWith(time)) { time += dateTime.slice(time.length).replace(/^(\p{P}*).*/u, '$1'); strs = [time, dateTime.slice(time.length).trimStart()]; } else if (dateTime.endsWith(time)) { strs = [dateTime.slice(0, -time.length).trimEnd(), time]; } else { return [dateTime]; } strs = strs.map(str => isWiki ? nowiki(str) : document.createTextNode(str)); strs.splice(1, 0, '<br>'); return strs; }; let nowiki = s => s.replace( /["&'<=>\[\]{|}]|:(?=\/\/)|_(?=_)|~(?=~~)/g, m => '&#' + m.codePointAt(0) + ';' ); let articlePath = mw.config.get('wgArticlePath').replace(/\$1.*/, ''); let getPath = (pathname, search, hash) => { let s = ''; if (pathname?.startsWith(articlePath)) { s = decodeURIComponent(pathname.slice(articlePath.length)); } else if (search) { let title = mw.util.getParamValue('title', search); if (title) { s = title; } } if (hash) { s += mw.util.percentDecodeFragment(hash); } return s.replace(/_/g, ' '); }; let api = new mw.Api({ // userAgent: 'MoveHistory (https://en.wikipedia.org/wiki/User:Nardog/MoveHistory)' }); let arrow = document.dir === 'rtl' ? ' ← ' : ' → '; class MoveHistorySearch { constructor(page, dir, since, until) { this.$status = $('<div>').appendTo(dialog.$results.empty()); this.page = page; this.ascending = dir === 'newer'; let sinceTs = (since || '2005-06-25') + 'T00:00:00Z'; let untilTs; if (until) { untilTs = until + 'T23:59:59Z'; } this.params = { action: 'query', titles: page, prop: 'revisions', rvstart: this.ascending ? sinceTs : untilTs, rvend: this.ascending ? untilTs : sinceTs, rvdir: dir, rvprop: 'sha1|timestamp|user|comment', rvlimit: 'max', formatversion: 2 }; this.revCount = 0; this.candidates = []; this.titles = {}; this.noRedirLinks = new WeakSet(); this.moves = []; this.start(); } start() { this.setBusy(true); dialog.actions.setMode('searching'); this.i = 0; this.paused = false; this.doNext(); } doNext() { if (!this.paused && this.candidates.length) { this.loadMoves(); } else if (!this.paused && !this.complete && this.i < 4) { this.loadRevs(); } else { this.finish(); } } async loadRevs() { this.i++; this.setStatus(`Loading history${ this.revCount ? this.ascending ? ' after ' + this.lastDate : ' before ' + this.firstDate : '' }...`); try { let response = await api.get(this.params); let revs = response.query?.pages?.[0]?.revisions; if (revs) { this.processRevs(revs); } this.params.rvcontinue = response.continue?.rvcontinue; if (!this.params.rvcontinue) { this.complete = response.batchcomplete; } this.doNext(); } catch (e) { let errorMsg = e?.error?.info || e; this.finish(`Error loading revisions${errorMsg ? ': ' + errorMsg : ''}`); } } processRevs(revs) { this.revCount += revs.length; if (!this.ascending) { revs.reverse(); } revs.forEach(rev => { let comp = this.lastRev; this.lastRev = rev; if (!rev.comment || !rev.user || !rev.sha1 || !comp || comp.sha1 !== rev.sha1 ) { return; } let matches = rev.comment.match(/\[\[:?([^\]]+)\]\].+?\[\[:?([^\]]+)\]\]/); if (matches) { rev.matches = matches.slice(1); } this.candidates.push(rev); }); if (!this.ascending || !this.firstDate) { this.firstDate = DateFormatter.formatTimeAndDate(new Date(revs[0].timestamp)); } if (this.ascending || !this.lastDate) { this.lastDate = DateFormatter.formatTimeAndDate(new Date(this.lastRev.timestamp)); } } async loadMoves() { let rev = this.candidates[this.ascending ? 'shift' : 'pop'](); this.setStatus(`Seeing if there was a move at ${ DateFormatter.formatTimeAndDate(new Date(rev.timestamp)) }...`); let date = Date.parse(rev.timestamp) / 1000; try { (await api.get({ action: 'query', list: 'logevents', letype: 'move', lestart: date + 60, leend: date, leprop: 'details|title|user|parsedcomment', lelimit: 'max', formatversion: 2 })).query?.logevents?.reverse().some(le => { if (le.user !== rev.user || !rev.comment.includes(le.title)) return; let target = le?.params?.target_title; if (!target || !rev.comment.includes(target) || rev.matches && [le.title, target].some(s => !rev.matches.includes(s)) ) { return; } this.addMove({ date: rev.timestamp, offset: new Date(Date.parse(rev.timestamp) + 1000) .toISOString().slice(0, -5).replace(/\D/g, ''), from: le.title, to: target, user: le.user, comment: $.parseHTML(le.parsedcomment) }); return true; }); this.doNext(); } catch (e) { let errorMsg = e?.error?.info || e; this.finish(`Error loading moves${errorMsg ? ': ' + errorMsg : ''}`); } } addMove(move) { if (!this.moves.length) { this.lastName = this.ascending ? move.from : move.to; this.$trail = $('<p>').append(this.makeLink(this.lastName)); this.$tbody = $('<tbody>'); this.$table = $('<table>').addClass('wikitable').append( $('<thead>').append( $('<tr>').append( $('<th>').attr('rowspan', 2).text('Date'), $('<th>').text(this.ascending ? 'From' : 'To'), $('<th>').attr('rowspan', 2).text('Performer'), $('<th>').attr('rowspan', 2).text('Comment') ), $('<tr>').append( $('<th>').text(this.ascending ? 'To' : 'From') ) ), this.$tbody ); this.$status.after('<hr>', this.$trail, this.$table); } if (this.ascending) { if (this.lastName !== move.from) { this.$trail.append(arrow + '?' + arrow, this.makeLink(move.from)); } this.$trail.append(arrow, this.makeLink(move.to)); this.lastName = move.to; } else { if (this.lastName !== move.to) { this.$trail.prepend(this.makeLink(move.to), arrow + '?' + arrow); } this.$trail.prepend(this.makeLink(move.from), arrow); this.lastName = move.from; } this.$tbody.append( $('<tr>').append( $('<td>').attr({ class: 'movehistory-date', rowspan: 2 }).append( $('<a>').attr({ href: mw.util.getUrl(this.page, { action: 'history', offset: move.offset }), title: 'See history up to this move', target: '_blank' }).append(wrapDate(move.date)) ), $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.from : move.to) ), $('<td>').attr({ class: 'movehistory-user', rowspan: 2 }).append( this.makeLink('User:' + move.user, move.user, true), '<br>', $('<span>').addClass('mw-changeslist-links').append( $('<span>').append(this.makeLink('User talk:' + move.user, 'talk', true)), $('<span>').append(this.makeLink('Special:Contributions/' + move.user, 'contribs', true)) ) ), $('<td>').attr({ class: 'movehistory-comment', rowspan: 2 }).append( $(move.comment).clone().attr('target', '_blank') ) ), $('<tr>').append( $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending ? move.to : move.from) ) ) ); dialog.setSize('large'); this.moves.push(move); } finish(error) { let count = this.moves.length; let complete = this.complete && !this.candidates.length; this.setStatus(error || `Found ${count} move${count === 1 ? '' : 's'} in ${ this.revCount.toLocaleString('en') } revisions${ this.revCount ? ` from ${this.firstDate} to ${this.lastDate}` : '' }.${ complete ? '' : ' Click Continue to inspect more revisions.' }`); this.setBusy(); this.mode = complete ? count ? 'found' : 'notFound' : count ? 'pausedFound' : 'paused'; dialog.actions.setMode(this.mode); if (!count) return; this.queryTitles( Object.entries(this.titles) .filter(([, v]) => !v.processed).map(([k]) => k) ); } setBusy(busy) { MoveHistoryDialog.static.escapable = !busy; dialog.$navigation.toggleClass('oo-ui-pendingElement-pending', !!busy); } setStatus(text) { this.$status.text(text); dialog.updateSize(); console.log(text); } makeLink(title, text, allowRedirect) { let obj; if (Object.hasOwn(this.titles, title)) { obj = this.titles[title]; } else { obj = { links: [] }; this.titles[title] = obj; if (title === this.page) { obj.classes = ['mw-selflink', 'selflink']; obj.processed = true; } } let params = obj.red && { action: 'edit', redlink: 1 } || !allowRedirect && obj.redirect && { redirect: 'no' }; let $link = $('<a>').attr({ href: mw.util.getUrl(obj.canonical || title, params), title: obj.canonical || title, target: '_blank' }).addClass(obj.classes).text(text || title); if (!allowRedirect && !obj.processed) { this.noRedirLinks.add($link[0]); } if (!obj.processed) { obj.links.push($link[0]); } return $link; } queryTitles(titles) { if (!titles.length) return; let curTitles = titles.slice(0, 50); curTitles.forEach(title => { this.titles[title].processed = true; }); let { query } = api.post({ action: 'query', titles: curTitles, prop: 'info', inprop: 'linkclasses', inlinkcontext: this.page, formatversion: 2 }, { headers: { 'Promise-Non-Write-API-Action': 1 } }); query?.normalized?.forEach(entry => { if (!Object.hasOwn(this.titles, entry.from)) return; let obj = this.titles[entry.from]; obj.canonical = entry.to; this.titles[entry.to] = obj; }); query?.pages?.forEach(page => { if (!Object.hasOwn(this.titles, page.title)) return; let obj = this.titles[page.title]; let classes = page.linkclasses || []; if (page.missing && !page.known) { classes.push('new'); obj.red = true; } else if (classes.includes('mw-redirect')) { obj.redirect = true; } if (classes.length) { obj.classes = classes; } }); curTitles.forEach(title => { let obj = this.titles[title]; let $links = $(obj.links).addClass(obj.classes); $links.attr('href', i => mw.util.getUrl( obj.canonical || title, obj.red && { action: 'edit', redlink: 1 } || obj.redirect && this.noRedirLinks.has($links[i]) && { redirect: 'no' } )); if (obj.canonical) { $links.attr('title', obj.canonical); } delete obj.links; }); this.queryTitles(titles.slice(50)); } copyResults() { let text = this.$trail.contents().get().map(n => ( n.tagName === 'A' ? `[[:${n.textContent}]]` : n.textContent )).join('') + ` {| class="wikitable plainlinks" ! rowspan="2" | Date ! ${this.ascending ? 'From' : 'To'} ! rowspan="2" | Performer ! rowspan="2" | Comment |- ! ${this.ascending ? 'To' : 'From'} ${this.moves.map(move => `|- | rowspan="2" style="white-space: nowrap;" | [{{fullurl:${this.page}|action=history&offset=${move.offset}}} ${wrapDate(move.date, true).join('')}] | style="text-align: center;" | ${ this.titles[this.ascending ? move.from : move.to] && this.titles[this.ascending ? move.from : move.to].redirect ? `[{{fullurl:${this.ascending ? move.from : move.to}|redirect=no}} ${this.ascending ? move.from : move.to}]` : `[[:${this.ascending ? move.from : move.to}]]` } | rowspan="2" style="white-space: nowrap;" | [[User:${move.user}|${move.user}]]<br>([[User talk:${move.user}|talk]] &#124; [[Special:Contributions/${move.user}|contribs]]) | rowspan="2" ${ move.comment.length ? 'style="vertical-align: top;" | ' + move.comment.map(n => ( n.tagName === 'A' ? `[[:${ n.classList.contains('extiw') ? n.title : getPath(n.pathname, n.search, n.hash) }|${nowiki(n.textContent)}]]` : nowiki(n.textContent) )).join('') : '|' } |- | style="text-align: center;" | ${ this.titles[this.ascending ? move.to : move.from] && this.titles[this.ascending ? move.to : move.from].redirect ? `[{{fullurl:${this.ascending ? move.to : move.from}|redirect=no}} ${this.ascending ? move.to : move.from}]` : `[[:${this.ascending ? move.to : move.from}]]` } `).join('')}|}`; let $textarea = $('<textarea>').attr({ readonly: '', style: 'position:fixed;top:-100%' }).val(text).appendTo(document.body); $textarea[0].select(); let copied; try { copied = document.execCommand('copy'); } catch (e) {} $textarea.remove(); if (copied) { mw.notify('Copied'); } else { mw.notify('Copy failed', { type: 'error' }); } } } function MoveHistoryDialog(config) { MoveHistoryDialog.parent.call(this, config); this.$element.addClass('movehistory'); } OO.inheritClass(MoveHistoryDialog, OO.ui.ProcessDialog); MoveHistoryDialog.static.name = 'moveHistoryDialog'; MoveHistoryDialog.static.title = 'Move history'; MoveHistoryDialog.static.size = 'small'; MoveHistoryDialog.static.actions = [ { modes: 'config', flags: ['safe', 'close'] }, { action: 'search', label: 'Search', modes: 'config', flags: ['primary', 'progressive'], disabled: true }, { action: 'goBack', modes: ['paused', 'pausedFound', 'found', 'notFound'], flags: ['safe', 'back'] }, { action: 'continue', label: 'Continue', modes: ['paused', 'pausedFound'], flags: ['primary', 'progressive'] }, { action: 'pause', label: 'Pause', modes: 'searching', flags: ['primary', 'destructive'] }, { action: 'copy', modes: ['pausedFound', 'found'], label: 'Copy results as wikitext' } ]; MoveHistoryDialog.prototype.initialize = function () { MoveHistoryDialog.parent.prototype.initialize.apply(this, arguments); let rt = mw.Title.newFromText(mw.config.get('wgRelevantPageName')); let defaultValue = rt && rt.namespace >= 0 ? rt.toText() : ''; this.pageInput = new mw.widgets.TitleInputWidget({ $overlay: this.$overlay, api: api, excludeDynamicNamespaces: true, required: true, showMissing: false, value: defaultValue }).connect(this, { change: 'updateButton' }).on('change', OO.ui.debounce(value => { this.switchButton.setDisabled(!value || !this.pageInput.getMWTitle()); }, 250)); this.switchButton = new OO.ui.ButtonWidget({ disabled: !defaultValue, label: 'Subject ↔ Talk' }).on('click', () => { let t = this.pageInput.getMWTitle(); let ot = t?.[t.isTalkPage() ? 'getSubjectPage' : 'getTalkPage'](); if (!ot) return; this.pageInput.setValue(ot.toText()); }); this.directionInput = new OO.ui.RadioSelectInputWidget({ options: [ { data: 'newer', label: 'Oldest first' }, { data: 'older', label: 'Newest first' } ] }); this.sinceInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD' }).connect(this, { flag: 'updateButton' }); this.untilInput = new mw.widgets.DateInputWidget({ $overlay: this.$overlay, displayFormat: 'YYYY-MM-DD', mustBeAfter: '2005-06-24' }).on('change', () => { let m = this.untilInput.getMoment(); this.sinceInput.mustBeBefore = m.isValid() ? m.add(1, 'days') : undefined; this.sinceInput.setValidityFlag(); }).connect(this, { flag: 'updateButton' }); this.form = new OO.ui.FormLayout({ items: [ new OO.ui.FieldLayout(this.pageInput, { label: 'Page:', align: 'top' }), this.switchButton, new OO.ui.FieldLayout(this.directionInput, { label: 'Direction:', align: 'top' }), new OO.ui.FieldLayout(this.sinceInput, { label: 'Since:', align: 'top' }), new OO.ui.FieldLayout(this.untilInput, { label: 'Until:', align: 'top' }) ] }).connect(this, { submit: ['executeAction', 'search'] }); this.$results = $('<div>'); this.form.$element .append($('<input>').attr({ type: 'submit', hidden: '' })) .appendTo(this.$body); }; MoveHistoryDialog.prototype.updateButton = function () { this.actions.setAbilities({ search: this.canSearch() }); }; MoveHistoryDialog.prototype.canSearch = function () { return !!this.pageInput.getValue() && !this.sinceInput.hasFlag('invalid') && !this.untilInput.hasFlag('invalid'); }; MoveHistoryDialog.prototype.getSetupProcess = function (data) { return MoveHistoryDialog.super.prototype.getSetupProcess.call(this, data).next(function () { this.updateButton(); this.actions.setMode('config'); }, this); }; MoveHistoryDialog.prototype.getReadyProcess = function (data) { return MoveHistoryDialog.super.prototype.getReadyProcess.call(this, data).next(function () { this.pageInput.focus(); }, this); }; MoveHistoryDialog.prototype.getActionProcess = function (action) { if (action === 'search') { if (!this.canSearch()) return; let config = [ this.pageInput.getMWTitle().toText(), this.directionInput.getValue(), this.sinceInput.getValue(), this.untilInput.getValue() ]; if (!this.config || config.some((v, i) => v !== this.config[i])) { this.config = config; this.search = new MoveHistorySearch(...config); } else { this.actions.setMode(this.search.mode); } this.form.toggle(false).$element.after(this.$results); this.setSize(this.search.moves.length ? 'large' : 'medium'); } else if (action === 'continue') { this.search.start(); } else if (action === 'pause') { this.search.paused = true; } else if (action === 'copy') { this.search.copyResults(); } else { this.actions.setMode('config'); this.$results.detach(); this.form.toggle(true); this.setSize('small'); } return MoveHistoryDialog.super.prototype.getActionProcess.call(this, action); }; dialog = new MoveHistoryDialog(); window.moveHistoryDialog = dialog; let winMan = new OO.ui.WindowManager(); winMan.addWindows([dialog]); winMan.$element.appendTo(OO.ui.getTeleportTarget()); dialog.open(); }); ha566uwiluulk5fry3dakd3ndfslc5l User:SongVĩ.Bot II 2 124239 760286 760157 2026-09-04T17:00:55Z SongVĩ.Bot II 52414 [[User:SongVĩ.Bot II|Task 0]]: Đã 1712 ngày... 760286 wikitext text/x-wiki Cập nhật lần cuối: 05-09-2026 Đã 1712 ngày... hst2ei6jeq5karz054jzw7y2nzkbpct NodeMW client 0 127112 760313 755049 2026-09-04T19:01:01Z ~2026-48185-51 75525 Testing nodemw client 760313 wikitext text/x-wiki Test Content 50690 --[[Special:Contributions/~2026-48185-51|~2026-48185-51]] ([[User talk:~2026-48185-51|talk]]) 19:01, 4 September 2026 (UTC) 47c8urb8kw46uyg2fajyn0wft2bqlwt Aiuto:Pagina di discussione 0 145659 760285 741036 2026-09-04T16:12:59Z Sàádî 69030 ([[c:GR|GR]]) [[c:COM:FR|File renamed]]: [[File:Itwiki help discusssion vector2022 2.png]] → [[File:Itwiki help discussion vector2022 2.png]] [[c:COM:FR#FR3|Criterion 3]] (obvious error) 760285 wikitext text/x-wiki Le '''pagine di discussione''' (a volte chiamate col nome inglese '''''talk''''') sono spazi utilizzati per il dialogo e il coordinamento tra gli utenti di Wikipedia. Eccetto le [[Aiuto:Pagine speciali|pagine speciali]] che hanno funzioni tecniche, tutte le altre pagine di Wikipedia ([[aiuto:voce|voci]], [[Wikipedia:pagina utente|pagine utente]], [[Wikipedia:Progetto|progetti tematici]], [[Aiuto:Categorie|categorie]]) dispongono di una relativa pagina di discussione nella quale chiunque può inserire commenti o fare proposte utili per la redazione o modifica dei contenuti relativi. Ad esempio, in "[[Discussione:Ignazio Silone]]" si discutono i contenuti della voce "[[Ignazio Silone]]". Le pagine di discussione fungono anche da luogo di confronto tra gli utenti per discutere problemi e trovare soluzioni, soprattutto nel caso di argomenti controversi. Quando vi sono pareri discordanti o interpretazioni differenti sul contenuto di una pagina, gli utenti non devono ingaggiare una [[Wikipedia:Guerra di modifiche|guerra di modifiche]], ma devono utilizzare la pagina di discussione per dialogare e stabilire il [[wp:consenso|consenso]] necessario a raggiungere una versione condivisa. Si badi bene: una pagina di discussione '''non è un forum''', ma serve solo a discutere i contenuti e a coordinare lo sviluppo di una determinata pagina. == Le principali pagine di discussione == {{tl|Vedi anche|Aiuto:Dove fare una domanda}} Su Wikipedia vi sono molte pagine di discussione, ciascuna con scopi precisi. Le più importanti tipologie sono: * [[#Le pagine di discussione delle voci|pagine di discussione delle voci]], dove si discutono le modifiche relative a ciascuna [[Aiuto:Voce|voce]] di Wikipedia. Lo stesso vale per i template e le categorie; * [[#Le pagine di discussione dei progetti|pagine di discussione dei progetti]], comunemente chiamate "bar tematici", dove si discute di argomenti generali relativi a insiemi di voci che trattano lo stesso tema (come in [[Discussioni progetto:Cinema]]) oppure di questioni relative al funzionamento di Wikipedia (è il caso dei molti "[[:categoria:Progetti di servizio|progetti di servizio]]"); * [[#Le pagine di discussione degli utenti|pagine di discussione degli utenti]], impiegate dai wikipediani per scambiarsi tra loro messaggi inerenti alle proprie attività su Wikipedia, inviando e ricevendo messaggi. Vi sono anche altri spazi dedicati al dialogo tra gli utenti i quali, pur avendo una funzione simile, non sono pagine di discussione in senso stretto, ma hanno comunque un utilizzo simile. Ad esempio: * per discussioni e segnalazioni di interesse generale su Wikipedia c'è il [[#Il bar di Wikipedia|bar di Wikipedia]]; * per informazioni su "come fare qualcosa" su Wikipedia c'è lo [[Aiuto:Sportello informazioni|sportello informazioni]]; * per domande su qualsiasi argomento non sia inerente al funzionamento di Wikipedia c'è l'[[Wikipedia:Oracolo|oracolo]]; * per proporre voci per la vetrina ci sono le [[Wikipedia:Riconoscimenti di qualità/Segnalazioni|segnalazioni per i riconoscimenti di qualità]]; * per le richieste di revisioni dettagliate sulle voci, c'è la [[Wikipedia:Vaglio|pagina dei vagli]]. == Dove si trova la pagina di discussione? == <div class="codex-tabs-sections"> ==Desktop (default)== [[File:Itwiki_help_discusssion_vector2022_1.png|thumb|upright=1.7|left|link=|La linguetta su cui cliccare per entrare nella pagina di discussione in cima alla pagina]] [[File:Itwiki help discussion vector2022 2.png|thumb|upright=1.7|right|link=|Il pulsante su cui cliccare per entrare nella pagina di discussione dalla barra di navigazione]] {{clear}} In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta <tvar name="1">"'''{{Int|talk|lang=it}}'''"</tvar>. In alternativa, quando ci si sposta verso il basso comparirà una barra di navigazione statica, che contiene il pulsante <tvar name=1>'''[[File:OOjs_UI_icon_speechBubbles-ltr.svg|link=|alt={{int|talk|lang=it}}]] {{int|talk|lang=it}}'''</tvar> Questi sono i pulsanti da cliccare per accedere alla pagina di discussione. Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. ==Desktop (Vector legacy)== [[File:Pulsante Discussione.png|thumb|upright=2.7|center|link=|La linguetta su cui cliccare per entrare nella pagina di discussione di una voce]] In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta "'''Discussione'''". Quello è il link da cliccare per accedere alla pagina di discussione. Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. ==Mobile== [[File:Itwiki_help_discusssion_mobile.jpg|thumb|upright=1.7|center|link=|La linguetta su cui cliccare per entrare nella pagina di discussione di una voce]] In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta "'''Discussione'''". Quello è il link da cliccare per accedere alla pagina di discussione. '''È necessario essere [[Aiuto:Registrazione|registrati]] per visualizzarla.''' Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. </div> === Indirizzo === {{Tipi di pagina}} L'indirizzo della discussione dipende dal [[Aiuto:namespace|tipo di pagina]] a cui la discussione fa riferimento (si veda il box a lato). * Per una [[Aiuto:voce|voce]] titolata "XXXX", l'indirizzo è <code><nowiki>[[Discussione:XXXX]]</nowiki></code>; : (esempio: [[Comunicazione]] e [[Discussione:Comunicazione]]). * Per una pagina di servizio (come Categoria, Progetto, Wikipedia, Aiuto ecc.) quale ad esempio "Categoria:YYY", la discussione si trova a <code><nowiki>[[Discussioni Categoria:YYY]]</nowiki></code> : (esempi: [[Aiuto:Tabelle]] e [[Discussioni Aiuto:Tabelle]]; [[Wikipedia:Vetrina]] e [[Discussioni Wikipedia:Vetrina]]). Per ricercare o linkare direttamente una pagina di discussione basta copiarne direttamente il titolo. Per linkare una specifica [[Aiuto:Sezione|sezione o paragrafo]] all'interno di una pagina di discussione, si deve usare questo codice: <code><nowiki>[[Titolo della pagina di discussione#titolo della sezione]]</nowiki></code>. Esempio (clicca per provare): [[Discussioni aiuto:Pagina di discussione#Esempio di paragrafo]]. {{Avviso|tipo=contenuto|immagine=[[File:Bombilla amarilla - yellow Edison lamp.svg|40px|!]]|testo='''Lo sapevi che''' quando una pagina viene seguita attraverso la funzione [[Aiuto:Osservati speciali|osservati speciali]], viene automaticamente seguita anche la relativa pagina di discussione?}} === Abbreviazioni === Per linkare pagine di discussione relative ad alcuni [[Aiuto:namespace|namespace]] come Progetto e Wikipedia è possibile usare abbreviazioni, ad esempio: * [[dp:Roma]] invece di [[Discussioni progetto:Roma]]. * [[dw:Vetrina]] invece di [[Discussioni wikipedia:Vetrina]] * ecc. Inoltre, per tutte le pagine di discussione si possono usare i nomi originali inglesi con il breve ''Talk'', ad esempio: * [[talk:Mary Poppins (serie di romanzi)]] * [[template talk:Cita]]. == Come si partecipa a una discussione == === Rispetta sempre i tuoi interlocutori === {{Vedi anche|Wikipedia:Wikiquette|Wikipedia: Niente attacchi personali}} [[file:Diskussion-Icon XY.svg|thumb|75px|L'enciclopedia si arricchisce grazie alle diversità di pensiero]] Prima di intervenire in qualunque pagina di discussione, si ricordino due principi fondamentali. * Gli interventi sulle pagine di discussione devono sempre rispettare la [[Aiuto:Wikiquette|'''Wikiquette''']]: ciò significa anche aderire allo spirito di collaborazione e mutua comprensione di Wikipedia (il cosiddetto [[Aiuto:Wikilove|'''Wikilove''']]). * Le discussioni servono a discutere temi di interesse per Wikipedia. Utenti diversi possono avere punti di vista differenti su un dato argomento, ma nelle discussioni si commentano e confrontano sempre e soltanto i contenuti, mai gli autori dei messaggi. Negli spazi di discussione non sono ammesse provocazioni, ''[[Flame (Internet)|flame]]'' (risse verbali) e men che meno [[Wikipedia:Niente attacchi personali|'''attacchi personali''']] contro altri utenti. È indispensabile accettare il fatto che siamo tutti parte della stessa comunità. Siamo tutti wikipediani: tu lo sei ugualmente a come lo sono tutti gli altri, '''qualunque sia il tuo bagaglio culturale o personale'''. Su Wikipedia non esistono ''esperti'', ma solo fonti attendibili e terze. Non squalificare il tuo operato su Wikipedia attraverso atteggiamenti e toni poco consoni ad un progetto collaborativo! === Organizzazione della pagina === Le pagine di discussione sono organizzate secondo pochi semplici criteri: * Ogni argomento si discute all'interno di una [[aiuto:sezione|sezione]] apposita. * Per inserire un ''nuovo'' argomento, clicca sulla linguetta "'''Aggiungi discussione'''" posta in alto nella schermata<ref>Se usi la [[wikipedia:Skin|skin monobook]] sarà quella contrassegnata con un "+".</ref>. Comparirà allora una nuova finestra con una riga denominata "Oggetto/intestazione", dove inserire ''il titolo'' della nuova discussione, e una finestra più grande in cui inserire il testo del proprio messaggio. * Tutti i messaggi vanno sempre '''[[aiuto:Firma|firmati]]'''. * Non si cancellano i messaggi degli altri utenti. * Ogni nuovo intervento va inserito in fondo alla discussione in corso, in modo che la discussione sia organizzata automaticamente in ordine cronologico, dall'intervento più vecchio a quello più recente. === Scrivere un messaggio === <div class="mw-gadget-tabbedwindow"> ===Strumenti di discussione (desktop, default)=== [[File:Itwiki_help_reply_tools.png|thumb|upright=1.7|center|link=|Esempio di utilizzo degli strumenti di discussione]] Utilizzando gli '''strumenti di discussione''', attivati di default per tutte le utenze e attivabile/disattivabile dalla sezione ''Pagine di discussione'' nelle [[Speciale:Preferenze#mw-prefsection-editing|preferenze sulla casella di modifica]] e dalla sezione ''Strumenti di discussione'' delle [[Speciale:Preferenze#mw-prefsection-betafeatures|funzionalità beta]], è possibile: * Rispondere a dei messaggi premendo il pulsante <code>'''[rispondi]'''</code> posto in seguito ad ogni messaggio; nel caso il messaggio non sia firmato, il pulsante non apparirà e dunque sarà necessario procedere con la [[#Modifica manuale (desktop)|modifica manuale]]. * Creare nuove discussioni premendo la linguetta "'''Aggiungi discussione'''" o aprendo una pagina di discussione non esistente. * Ricevere una notifica ogni volta che viene aggiunto un messaggio in una discussione premendo il pulsante <code>'''[iscriviti]'''</code>, situato all'estremo destro dello schermo, alla stessa altezza del titolo della sezione. ===Modifica manuale (desktop)=== [[File:Itwiki_help_reply_desktop.png|thumb|upright=1.7|right|link=|Esempio di risposta attraverso la modifica manuale]] [[File:Itwiki help newsection desktop.png|thumb|upright=1.7|left|link=|Dimostrazione di come si aggiunge una nuova discussione]] {{Avviso |testo = '''Lo sapevi che''' su Wikipedia esiste un [[Aiuto:Tour guidato|Tour guidato]] per imparare in fretta ad usare le pagine di discussione? '''[[Aiuto:Tour_guidato/Tutorial_2-1|Clicca qui per provare!]]''' |tipo = contenuto |immagine = [[File:Bombilla amarilla - yellow Edison lamp.svg|40px|!]] }} Per partecipare ad una discussione utilizzando il '''metodo manuale''', bisogna [[Aiuto:Modifica|modificare]] la pagina di discussione. Ciò avviene in modo identico ad una pagina tradizionale, attraverso la linguetta "'''{{MediaWiki:Visualeditor-ca-editsource}}'''" posta nella parte alta della schermata, oppure utilizzando il pulsante "<code>[modifica]</code>" posto fianco del titolo di una delle sezioni che compongono la pagina. Si può iniziare una nuova discussione premendo la linguetta "'''Aggiungi discussione'''" o aprendo una pagina di discussione non esistente. Per facilitare la lettura della pagina, si raccomanda di usare i seguenti accorgimenti: * Lasciare sempre una riga orizzontale vuota tra un messaggio e l'altro; * [[Indentazione|indentare]] ogni nuovo messaggio verso destra, inserendo uno o più segni di "<code>:</code>" (due punti) ''all'inizio del paragrafo''. (vedi la sezione [[#Indentazione della discussione]] qui sotto). '''Chiunque''' scriva in una pagina di discussione '''deve sempre firmare''' il proprio messaggio prima di salvare la pagina. {{vedi anche|Aiuto:Firma}} Solo quando si risponde utilizzando il metodo manuale è opportuno inserire manualmente la firma. [[File:Per firmare.png|thumb|upright=2.7|Clicca sempre '''questo bottone''' quando hai finito di scrivere il tuo intervento e prima di salvare la pagina.]] Ci sono due modi per scrivere la propria firma: * Finito di scrivere, clicca sul pulsante raffigurante una penna, posizionato in alto nella finestra di modifica. * Finito di scrivere, aggiungi manualmente <code><nowiki>"--~~~~"</nowiki></code> alla fine del tuo messaggio. Dopo aver salvato la pagina, al posto di <nowiki>"--~~~~"</nowiki> compariranno il nome utente di chi ha scritto il messaggio, insieme alla data e all'ora di salvataggio della pagina. Se non sei un utente registrato, non hai effettuato il [[Aiuto:Login|login]] o la tua sessione non è più attiva, al posto del nome utente comparirà il tuo indirizzo IP. Per verificare di essere connessi con la propria utenza basta premere il pulsante [[Aiuto:Anteprima|anteprima]] della pagina prima di salvarla e verificare che al posto della firma <nowiki>"~~~~"</nowiki> appaia il proprio nome utente. {{Avviso |tipo = importante |immagine = [[File:Emblem-important-red.svg|40px|Smile]] | testo = Usa la firma sempre soltanto nelle pagine di discussione, '''MAI''' nelle voci di Wikipedia. ''(Le voci sono prodotti comunitari, non vanno mai firmate!)'' }} ===Mobile=== [[File:Itwiki_help_reply_mobile.jpg|thumb|upright=1|right|link=|Esempio di risposta da mobile]] Per partecipare ad una discussione utilizzando '''dispositivi mobili''', bisogna aprire la pagina di discussione, premere sul titolo della sezione interessata e scrivere la risposta nel box in fondo alla pagina. Si può iniziare una nuova discussione premendo il pulsante "'''Aggiungi argomento'''" posto in cima alla pagina di discussione o aprendo una pagina di discussione non esistente. Per facilitare la lettura della pagina, si raccomanda di [[Indentazione|indentare]] ogni nuovo messaggio verso destra, inserendo uno o più segni di "<code>:</code>" (due punti) ''all'inizio del paragrafo''. (vedi la sezione [[#Indentazione della discussione]] qui sotto). Oppure selezionando l'opzione "Leggi come pagina Wiki" è possibile, cliccando sul simbolo della matita '''[[File:OOjs_UI_icon_edit-ltr.svg|link=|alt={{int|vector-view-edit|lang=it}}]]''', aggiungere il commento come spiegato in [[#Modifica manuale (desktop)|modifica manuale]]. Se non si accede a Wikipedia dal web ma dall'applicazione per dispositivi mobili, il pulsante per accedere alle pagine di discussione sarà situato in fondo alle voci. </div> '' [ommiss] '' CC-BY-SA 4.0 [[:it:Aiuto:Pagina di discussione]] m7x6eyakj5am0e15gukz3jylhfgmsgs 760392 760285 2026-09-05T04:25:07Z Rachmat04 26096 ([[c:GR|GR]]) [[c:COM:FR|File renamed]]: [[File:Itwiki help discusssion mobile.jpg]] → [[File:Itwiki help discussion mobile.jpg]] [[c:COM:FR#FR3|Criterion 3]] (obvious error) 760392 wikitext text/x-wiki Le '''pagine di discussione''' (a volte chiamate col nome inglese '''''talk''''') sono spazi utilizzati per il dialogo e il coordinamento tra gli utenti di Wikipedia. Eccetto le [[Aiuto:Pagine speciali|pagine speciali]] che hanno funzioni tecniche, tutte le altre pagine di Wikipedia ([[aiuto:voce|voci]], [[Wikipedia:pagina utente|pagine utente]], [[Wikipedia:Progetto|progetti tematici]], [[Aiuto:Categorie|categorie]]) dispongono di una relativa pagina di discussione nella quale chiunque può inserire commenti o fare proposte utili per la redazione o modifica dei contenuti relativi. Ad esempio, in "[[Discussione:Ignazio Silone]]" si discutono i contenuti della voce "[[Ignazio Silone]]". Le pagine di discussione fungono anche da luogo di confronto tra gli utenti per discutere problemi e trovare soluzioni, soprattutto nel caso di argomenti controversi. Quando vi sono pareri discordanti o interpretazioni differenti sul contenuto di una pagina, gli utenti non devono ingaggiare una [[Wikipedia:Guerra di modifiche|guerra di modifiche]], ma devono utilizzare la pagina di discussione per dialogare e stabilire il [[wp:consenso|consenso]] necessario a raggiungere una versione condivisa. Si badi bene: una pagina di discussione '''non è un forum''', ma serve solo a discutere i contenuti e a coordinare lo sviluppo di una determinata pagina. == Le principali pagine di discussione == {{tl|Vedi anche|Aiuto:Dove fare una domanda}} Su Wikipedia vi sono molte pagine di discussione, ciascuna con scopi precisi. Le più importanti tipologie sono: * [[#Le pagine di discussione delle voci|pagine di discussione delle voci]], dove si discutono le modifiche relative a ciascuna [[Aiuto:Voce|voce]] di Wikipedia. Lo stesso vale per i template e le categorie; * [[#Le pagine di discussione dei progetti|pagine di discussione dei progetti]], comunemente chiamate "bar tematici", dove si discute di argomenti generali relativi a insiemi di voci che trattano lo stesso tema (come in [[Discussioni progetto:Cinema]]) oppure di questioni relative al funzionamento di Wikipedia (è il caso dei molti "[[:categoria:Progetti di servizio|progetti di servizio]]"); * [[#Le pagine di discussione degli utenti|pagine di discussione degli utenti]], impiegate dai wikipediani per scambiarsi tra loro messaggi inerenti alle proprie attività su Wikipedia, inviando e ricevendo messaggi. Vi sono anche altri spazi dedicati al dialogo tra gli utenti i quali, pur avendo una funzione simile, non sono pagine di discussione in senso stretto, ma hanno comunque un utilizzo simile. Ad esempio: * per discussioni e segnalazioni di interesse generale su Wikipedia c'è il [[#Il bar di Wikipedia|bar di Wikipedia]]; * per informazioni su "come fare qualcosa" su Wikipedia c'è lo [[Aiuto:Sportello informazioni|sportello informazioni]]; * per domande su qualsiasi argomento non sia inerente al funzionamento di Wikipedia c'è l'[[Wikipedia:Oracolo|oracolo]]; * per proporre voci per la vetrina ci sono le [[Wikipedia:Riconoscimenti di qualità/Segnalazioni|segnalazioni per i riconoscimenti di qualità]]; * per le richieste di revisioni dettagliate sulle voci, c'è la [[Wikipedia:Vaglio|pagina dei vagli]]. == Dove si trova la pagina di discussione? == <div class="codex-tabs-sections"> ==Desktop (default)== [[File:Itwiki_help_discusssion_vector2022_1.png|thumb|upright=1.7|left|link=|La linguetta su cui cliccare per entrare nella pagina di discussione in cima alla pagina]] [[File:Itwiki help discussion vector2022 2.png|thumb|upright=1.7|right|link=|Il pulsante su cui cliccare per entrare nella pagina di discussione dalla barra di navigazione]] {{clear}} In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta <tvar name="1">"'''{{Int|talk|lang=it}}'''"</tvar>. In alternativa, quando ci si sposta verso il basso comparirà una barra di navigazione statica, che contiene il pulsante <tvar name=1>'''[[File:OOjs_UI_icon_speechBubbles-ltr.svg|link=|alt={{int|talk|lang=it}}]] {{int|talk|lang=it}}'''</tvar> Questi sono i pulsanti da cliccare per accedere alla pagina di discussione. Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. ==Desktop (Vector legacy)== [[File:Pulsante Discussione.png|thumb|upright=2.7|center|link=|La linguetta su cui cliccare per entrare nella pagina di discussione di una voce]] In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta "'''Discussione'''". Quello è il link da cliccare per accedere alla pagina di discussione. Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. ==Mobile== [[File:Itwiki help discussion mobile.jpg|thumb|upright=1.7|center|link=|La linguetta su cui cliccare per entrare nella pagina di discussione di una voce]] In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta "'''Discussione'''". Quello è il link da cliccare per accedere alla pagina di discussione. '''È necessario essere [[Aiuto:Registrazione|registrati]] per visualizzarla.''' Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. </div> === Indirizzo === {{Tipi di pagina}} L'indirizzo della discussione dipende dal [[Aiuto:namespace|tipo di pagina]] a cui la discussione fa riferimento (si veda il box a lato). * Per una [[Aiuto:voce|voce]] titolata "XXXX", l'indirizzo è <code><nowiki>[[Discussione:XXXX]]</nowiki></code>; : (esempio: [[Comunicazione]] e [[Discussione:Comunicazione]]). * Per una pagina di servizio (come Categoria, Progetto, Wikipedia, Aiuto ecc.) quale ad esempio "Categoria:YYY", la discussione si trova a <code><nowiki>[[Discussioni Categoria:YYY]]</nowiki></code> : (esempi: [[Aiuto:Tabelle]] e [[Discussioni Aiuto:Tabelle]]; [[Wikipedia:Vetrina]] e [[Discussioni Wikipedia:Vetrina]]). Per ricercare o linkare direttamente una pagina di discussione basta copiarne direttamente il titolo. Per linkare una specifica [[Aiuto:Sezione|sezione o paragrafo]] all'interno di una pagina di discussione, si deve usare questo codice: <code><nowiki>[[Titolo della pagina di discussione#titolo della sezione]]</nowiki></code>. Esempio (clicca per provare): [[Discussioni aiuto:Pagina di discussione#Esempio di paragrafo]]. {{Avviso|tipo=contenuto|immagine=[[File:Bombilla amarilla - yellow Edison lamp.svg|40px|!]]|testo='''Lo sapevi che''' quando una pagina viene seguita attraverso la funzione [[Aiuto:Osservati speciali|osservati speciali]], viene automaticamente seguita anche la relativa pagina di discussione?}} === Abbreviazioni === Per linkare pagine di discussione relative ad alcuni [[Aiuto:namespace|namespace]] come Progetto e Wikipedia è possibile usare abbreviazioni, ad esempio: * [[dp:Roma]] invece di [[Discussioni progetto:Roma]]. * [[dw:Vetrina]] invece di [[Discussioni wikipedia:Vetrina]] * ecc. Inoltre, per tutte le pagine di discussione si possono usare i nomi originali inglesi con il breve ''Talk'', ad esempio: * [[talk:Mary Poppins (serie di romanzi)]] * [[template talk:Cita]]. == Come si partecipa a una discussione == === Rispetta sempre i tuoi interlocutori === {{Vedi anche|Wikipedia:Wikiquette|Wikipedia: Niente attacchi personali}} [[file:Diskussion-Icon XY.svg|thumb|75px|L'enciclopedia si arricchisce grazie alle diversità di pensiero]] Prima di intervenire in qualunque pagina di discussione, si ricordino due principi fondamentali. * Gli interventi sulle pagine di discussione devono sempre rispettare la [[Aiuto:Wikiquette|'''Wikiquette''']]: ciò significa anche aderire allo spirito di collaborazione e mutua comprensione di Wikipedia (il cosiddetto [[Aiuto:Wikilove|'''Wikilove''']]). * Le discussioni servono a discutere temi di interesse per Wikipedia. Utenti diversi possono avere punti di vista differenti su un dato argomento, ma nelle discussioni si commentano e confrontano sempre e soltanto i contenuti, mai gli autori dei messaggi. Negli spazi di discussione non sono ammesse provocazioni, ''[[Flame (Internet)|flame]]'' (risse verbali) e men che meno [[Wikipedia:Niente attacchi personali|'''attacchi personali''']] contro altri utenti. È indispensabile accettare il fatto che siamo tutti parte della stessa comunità. Siamo tutti wikipediani: tu lo sei ugualmente a come lo sono tutti gli altri, '''qualunque sia il tuo bagaglio culturale o personale'''. Su Wikipedia non esistono ''esperti'', ma solo fonti attendibili e terze. Non squalificare il tuo operato su Wikipedia attraverso atteggiamenti e toni poco consoni ad un progetto collaborativo! === Organizzazione della pagina === Le pagine di discussione sono organizzate secondo pochi semplici criteri: * Ogni argomento si discute all'interno di una [[aiuto:sezione|sezione]] apposita. * Per inserire un ''nuovo'' argomento, clicca sulla linguetta "'''Aggiungi discussione'''" posta in alto nella schermata<ref>Se usi la [[wikipedia:Skin|skin monobook]] sarà quella contrassegnata con un "+".</ref>. Comparirà allora una nuova finestra con una riga denominata "Oggetto/intestazione", dove inserire ''il titolo'' della nuova discussione, e una finestra più grande in cui inserire il testo del proprio messaggio. * Tutti i messaggi vanno sempre '''[[aiuto:Firma|firmati]]'''. * Non si cancellano i messaggi degli altri utenti. * Ogni nuovo intervento va inserito in fondo alla discussione in corso, in modo che la discussione sia organizzata automaticamente in ordine cronologico, dall'intervento più vecchio a quello più recente. === Scrivere un messaggio === <div class="mw-gadget-tabbedwindow"> ===Strumenti di discussione (desktop, default)=== [[File:Itwiki_help_reply_tools.png|thumb|upright=1.7|center|link=|Esempio di utilizzo degli strumenti di discussione]] Utilizzando gli '''strumenti di discussione''', attivati di default per tutte le utenze e attivabile/disattivabile dalla sezione ''Pagine di discussione'' nelle [[Speciale:Preferenze#mw-prefsection-editing|preferenze sulla casella di modifica]] e dalla sezione ''Strumenti di discussione'' delle [[Speciale:Preferenze#mw-prefsection-betafeatures|funzionalità beta]], è possibile: * Rispondere a dei messaggi premendo il pulsante <code>'''[rispondi]'''</code> posto in seguito ad ogni messaggio; nel caso il messaggio non sia firmato, il pulsante non apparirà e dunque sarà necessario procedere con la [[#Modifica manuale (desktop)|modifica manuale]]. * Creare nuove discussioni premendo la linguetta "'''Aggiungi discussione'''" o aprendo una pagina di discussione non esistente. * Ricevere una notifica ogni volta che viene aggiunto un messaggio in una discussione premendo il pulsante <code>'''[iscriviti]'''</code>, situato all'estremo destro dello schermo, alla stessa altezza del titolo della sezione. ===Modifica manuale (desktop)=== [[File:Itwiki_help_reply_desktop.png|thumb|upright=1.7|right|link=|Esempio di risposta attraverso la modifica manuale]] [[File:Itwiki help newsection desktop.png|thumb|upright=1.7|left|link=|Dimostrazione di come si aggiunge una nuova discussione]] {{Avviso |testo = '''Lo sapevi che''' su Wikipedia esiste un [[Aiuto:Tour guidato|Tour guidato]] per imparare in fretta ad usare le pagine di discussione? '''[[Aiuto:Tour_guidato/Tutorial_2-1|Clicca qui per provare!]]''' |tipo = contenuto |immagine = [[File:Bombilla amarilla - yellow Edison lamp.svg|40px|!]] }} Per partecipare ad una discussione utilizzando il '''metodo manuale''', bisogna [[Aiuto:Modifica|modificare]] la pagina di discussione. Ciò avviene in modo identico ad una pagina tradizionale, attraverso la linguetta "'''{{MediaWiki:Visualeditor-ca-editsource}}'''" posta nella parte alta della schermata, oppure utilizzando il pulsante "<code>[modifica]</code>" posto fianco del titolo di una delle sezioni che compongono la pagina. Si può iniziare una nuova discussione premendo la linguetta "'''Aggiungi discussione'''" o aprendo una pagina di discussione non esistente. Per facilitare la lettura della pagina, si raccomanda di usare i seguenti accorgimenti: * Lasciare sempre una riga orizzontale vuota tra un messaggio e l'altro; * [[Indentazione|indentare]] ogni nuovo messaggio verso destra, inserendo uno o più segni di "<code>:</code>" (due punti) ''all'inizio del paragrafo''. (vedi la sezione [[#Indentazione della discussione]] qui sotto). '''Chiunque''' scriva in una pagina di discussione '''deve sempre firmare''' il proprio messaggio prima di salvare la pagina. {{vedi anche|Aiuto:Firma}} Solo quando si risponde utilizzando il metodo manuale è opportuno inserire manualmente la firma. [[File:Per firmare.png|thumb|upright=2.7|Clicca sempre '''questo bottone''' quando hai finito di scrivere il tuo intervento e prima di salvare la pagina.]] Ci sono due modi per scrivere la propria firma: * Finito di scrivere, clicca sul pulsante raffigurante una penna, posizionato in alto nella finestra di modifica. * Finito di scrivere, aggiungi manualmente <code><nowiki>"--~~~~"</nowiki></code> alla fine del tuo messaggio. Dopo aver salvato la pagina, al posto di <nowiki>"--~~~~"</nowiki> compariranno il nome utente di chi ha scritto il messaggio, insieme alla data e all'ora di salvataggio della pagina. Se non sei un utente registrato, non hai effettuato il [[Aiuto:Login|login]] o la tua sessione non è più attiva, al posto del nome utente comparirà il tuo indirizzo IP. Per verificare di essere connessi con la propria utenza basta premere il pulsante [[Aiuto:Anteprima|anteprima]] della pagina prima di salvarla e verificare che al posto della firma <nowiki>"~~~~"</nowiki> appaia il proprio nome utente. {{Avviso |tipo = importante |immagine = [[File:Emblem-important-red.svg|40px|Smile]] | testo = Usa la firma sempre soltanto nelle pagine di discussione, '''MAI''' nelle voci di Wikipedia. ''(Le voci sono prodotti comunitari, non vanno mai firmate!)'' }} ===Mobile=== [[File:Itwiki_help_reply_mobile.jpg|thumb|upright=1|right|link=|Esempio di risposta da mobile]] Per partecipare ad una discussione utilizzando '''dispositivi mobili''', bisogna aprire la pagina di discussione, premere sul titolo della sezione interessata e scrivere la risposta nel box in fondo alla pagina. Si può iniziare una nuova discussione premendo il pulsante "'''Aggiungi argomento'''" posto in cima alla pagina di discussione o aprendo una pagina di discussione non esistente. Per facilitare la lettura della pagina, si raccomanda di [[Indentazione|indentare]] ogni nuovo messaggio verso destra, inserendo uno o più segni di "<code>:</code>" (due punti) ''all'inizio del paragrafo''. (vedi la sezione [[#Indentazione della discussione]] qui sotto). Oppure selezionando l'opzione "Leggi come pagina Wiki" è possibile, cliccando sul simbolo della matita '''[[File:OOjs_UI_icon_edit-ltr.svg|link=|alt={{int|vector-view-edit|lang=it}}]]''', aggiungere il commento come spiegato in [[#Modifica manuale (desktop)|modifica manuale]]. Se non si accede a Wikipedia dal web ma dall'applicazione per dispositivi mobili, il pulsante per accedere alle pagine di discussione sarà situato in fondo alle voci. </div> '' [ommiss] '' CC-BY-SA 4.0 [[:it:Aiuto:Pagina di discussione]] ooshfll6too373pe77qv242dngzjnxi 760393 760392 2026-09-05T04:25:33Z Rachmat04 26096 ([[c:GR|GR]]) [[c:COM:FR|File renamed]]: [[File:Itwiki help discusssion vector2022 1.png]] → [[File:Itwiki help discussion vector2022 1.png]] [[c:COM:FR#FR3|Criterion 3]] (obvious error) 760393 wikitext text/x-wiki Le '''pagine di discussione''' (a volte chiamate col nome inglese '''''talk''''') sono spazi utilizzati per il dialogo e il coordinamento tra gli utenti di Wikipedia. Eccetto le [[Aiuto:Pagine speciali|pagine speciali]] che hanno funzioni tecniche, tutte le altre pagine di Wikipedia ([[aiuto:voce|voci]], [[Wikipedia:pagina utente|pagine utente]], [[Wikipedia:Progetto|progetti tematici]], [[Aiuto:Categorie|categorie]]) dispongono di una relativa pagina di discussione nella quale chiunque può inserire commenti o fare proposte utili per la redazione o modifica dei contenuti relativi. Ad esempio, in "[[Discussione:Ignazio Silone]]" si discutono i contenuti della voce "[[Ignazio Silone]]". Le pagine di discussione fungono anche da luogo di confronto tra gli utenti per discutere problemi e trovare soluzioni, soprattutto nel caso di argomenti controversi. Quando vi sono pareri discordanti o interpretazioni differenti sul contenuto di una pagina, gli utenti non devono ingaggiare una [[Wikipedia:Guerra di modifiche|guerra di modifiche]], ma devono utilizzare la pagina di discussione per dialogare e stabilire il [[wp:consenso|consenso]] necessario a raggiungere una versione condivisa. Si badi bene: una pagina di discussione '''non è un forum''', ma serve solo a discutere i contenuti e a coordinare lo sviluppo di una determinata pagina. == Le principali pagine di discussione == {{tl|Vedi anche|Aiuto:Dove fare una domanda}} Su Wikipedia vi sono molte pagine di discussione, ciascuna con scopi precisi. Le più importanti tipologie sono: * [[#Le pagine di discussione delle voci|pagine di discussione delle voci]], dove si discutono le modifiche relative a ciascuna [[Aiuto:Voce|voce]] di Wikipedia. Lo stesso vale per i template e le categorie; * [[#Le pagine di discussione dei progetti|pagine di discussione dei progetti]], comunemente chiamate "bar tematici", dove si discute di argomenti generali relativi a insiemi di voci che trattano lo stesso tema (come in [[Discussioni progetto:Cinema]]) oppure di questioni relative al funzionamento di Wikipedia (è il caso dei molti "[[:categoria:Progetti di servizio|progetti di servizio]]"); * [[#Le pagine di discussione degli utenti|pagine di discussione degli utenti]], impiegate dai wikipediani per scambiarsi tra loro messaggi inerenti alle proprie attività su Wikipedia, inviando e ricevendo messaggi. Vi sono anche altri spazi dedicati al dialogo tra gli utenti i quali, pur avendo una funzione simile, non sono pagine di discussione in senso stretto, ma hanno comunque un utilizzo simile. Ad esempio: * per discussioni e segnalazioni di interesse generale su Wikipedia c'è il [[#Il bar di Wikipedia|bar di Wikipedia]]; * per informazioni su "come fare qualcosa" su Wikipedia c'è lo [[Aiuto:Sportello informazioni|sportello informazioni]]; * per domande su qualsiasi argomento non sia inerente al funzionamento di Wikipedia c'è l'[[Wikipedia:Oracolo|oracolo]]; * per proporre voci per la vetrina ci sono le [[Wikipedia:Riconoscimenti di qualità/Segnalazioni|segnalazioni per i riconoscimenti di qualità]]; * per le richieste di revisioni dettagliate sulle voci, c'è la [[Wikipedia:Vaglio|pagina dei vagli]]. == Dove si trova la pagina di discussione? == <div class="codex-tabs-sections"> ==Desktop (default)== [[File:Itwiki help discussion vector2022 1.png|thumb|upright=1.7|left|link=|La linguetta su cui cliccare per entrare nella pagina di discussione in cima alla pagina]] [[File:Itwiki help discussion vector2022 2.png|thumb|upright=1.7|right|link=|Il pulsante su cui cliccare per entrare nella pagina di discussione dalla barra di navigazione]] {{clear}} In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta <tvar name="1">"'''{{Int|talk|lang=it}}'''"</tvar>. In alternativa, quando ci si sposta verso il basso comparirà una barra di navigazione statica, che contiene il pulsante <tvar name=1>'''[[File:OOjs_UI_icon_speechBubbles-ltr.svg|link=|alt={{int|talk|lang=it}}]] {{int|talk|lang=it}}'''</tvar> Questi sono i pulsanti da cliccare per accedere alla pagina di discussione. Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. ==Desktop (Vector legacy)== [[File:Pulsante Discussione.png|thumb|upright=2.7|center|link=|La linguetta su cui cliccare per entrare nella pagina di discussione di una voce]] In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta "'''Discussione'''". Quello è il link da cliccare per accedere alla pagina di discussione. Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. ==Mobile== [[File:Itwiki help discussion mobile.jpg|thumb|upright=1.7|center|link=|La linguetta su cui cliccare per entrare nella pagina di discussione di una voce]] In qualunque pagina di Wikipedia ci si trovi (ad eccezione delle pagine speciali), nella parte alta della schermata c'è una linguetta che reca la scritta "'''Discussione'''". Quello è il link da cliccare per accedere alla pagina di discussione. '''È necessario essere [[Aiuto:Registrazione|registrati]] per visualizzarla.''' Per tornare alla pagina principale basterà cliccare sulla prima linguetta, posta subito prima della linguetta "Discussione" (il suo nome sarà diverso a seconda di dove ci si trova: "Voce", "Progetto", "Categoria" ecc). Se il testo "<span style="color:BA0000;">Discussione</span>" è rosso significa che la pagina di discussione non è ancora stata creata. Se si desidera crearla, basterà cliccare sul link e inserire il testo desiderato. </div> === Indirizzo === {{Tipi di pagina}} L'indirizzo della discussione dipende dal [[Aiuto:namespace|tipo di pagina]] a cui la discussione fa riferimento (si veda il box a lato). * Per una [[Aiuto:voce|voce]] titolata "XXXX", l'indirizzo è <code><nowiki>[[Discussione:XXXX]]</nowiki></code>; : (esempio: [[Comunicazione]] e [[Discussione:Comunicazione]]). * Per una pagina di servizio (come Categoria, Progetto, Wikipedia, Aiuto ecc.) quale ad esempio "Categoria:YYY", la discussione si trova a <code><nowiki>[[Discussioni Categoria:YYY]]</nowiki></code> : (esempi: [[Aiuto:Tabelle]] e [[Discussioni Aiuto:Tabelle]]; [[Wikipedia:Vetrina]] e [[Discussioni Wikipedia:Vetrina]]). Per ricercare o linkare direttamente una pagina di discussione basta copiarne direttamente il titolo. Per linkare una specifica [[Aiuto:Sezione|sezione o paragrafo]] all'interno di una pagina di discussione, si deve usare questo codice: <code><nowiki>[[Titolo della pagina di discussione#titolo della sezione]]</nowiki></code>. Esempio (clicca per provare): [[Discussioni aiuto:Pagina di discussione#Esempio di paragrafo]]. {{Avviso|tipo=contenuto|immagine=[[File:Bombilla amarilla - yellow Edison lamp.svg|40px|!]]|testo='''Lo sapevi che''' quando una pagina viene seguita attraverso la funzione [[Aiuto:Osservati speciali|osservati speciali]], viene automaticamente seguita anche la relativa pagina di discussione?}} === Abbreviazioni === Per linkare pagine di discussione relative ad alcuni [[Aiuto:namespace|namespace]] come Progetto e Wikipedia è possibile usare abbreviazioni, ad esempio: * [[dp:Roma]] invece di [[Discussioni progetto:Roma]]. * [[dw:Vetrina]] invece di [[Discussioni wikipedia:Vetrina]] * ecc. Inoltre, per tutte le pagine di discussione si possono usare i nomi originali inglesi con il breve ''Talk'', ad esempio: * [[talk:Mary Poppins (serie di romanzi)]] * [[template talk:Cita]]. == Come si partecipa a una discussione == === Rispetta sempre i tuoi interlocutori === {{Vedi anche|Wikipedia:Wikiquette|Wikipedia: Niente attacchi personali}} [[file:Diskussion-Icon XY.svg|thumb|75px|L'enciclopedia si arricchisce grazie alle diversità di pensiero]] Prima di intervenire in qualunque pagina di discussione, si ricordino due principi fondamentali. * Gli interventi sulle pagine di discussione devono sempre rispettare la [[Aiuto:Wikiquette|'''Wikiquette''']]: ciò significa anche aderire allo spirito di collaborazione e mutua comprensione di Wikipedia (il cosiddetto [[Aiuto:Wikilove|'''Wikilove''']]). * Le discussioni servono a discutere temi di interesse per Wikipedia. Utenti diversi possono avere punti di vista differenti su un dato argomento, ma nelle discussioni si commentano e confrontano sempre e soltanto i contenuti, mai gli autori dei messaggi. Negli spazi di discussione non sono ammesse provocazioni, ''[[Flame (Internet)|flame]]'' (risse verbali) e men che meno [[Wikipedia:Niente attacchi personali|'''attacchi personali''']] contro altri utenti. È indispensabile accettare il fatto che siamo tutti parte della stessa comunità. Siamo tutti wikipediani: tu lo sei ugualmente a come lo sono tutti gli altri, '''qualunque sia il tuo bagaglio culturale o personale'''. Su Wikipedia non esistono ''esperti'', ma solo fonti attendibili e terze. Non squalificare il tuo operato su Wikipedia attraverso atteggiamenti e toni poco consoni ad un progetto collaborativo! === Organizzazione della pagina === Le pagine di discussione sono organizzate secondo pochi semplici criteri: * Ogni argomento si discute all'interno di una [[aiuto:sezione|sezione]] apposita. * Per inserire un ''nuovo'' argomento, clicca sulla linguetta "'''Aggiungi discussione'''" posta in alto nella schermata<ref>Se usi la [[wikipedia:Skin|skin monobook]] sarà quella contrassegnata con un "+".</ref>. Comparirà allora una nuova finestra con una riga denominata "Oggetto/intestazione", dove inserire ''il titolo'' della nuova discussione, e una finestra più grande in cui inserire il testo del proprio messaggio. * Tutti i messaggi vanno sempre '''[[aiuto:Firma|firmati]]'''. * Non si cancellano i messaggi degli altri utenti. * Ogni nuovo intervento va inserito in fondo alla discussione in corso, in modo che la discussione sia organizzata automaticamente in ordine cronologico, dall'intervento più vecchio a quello più recente. === Scrivere un messaggio === <div class="mw-gadget-tabbedwindow"> ===Strumenti di discussione (desktop, default)=== [[File:Itwiki_help_reply_tools.png|thumb|upright=1.7|center|link=|Esempio di utilizzo degli strumenti di discussione]] Utilizzando gli '''strumenti di discussione''', attivati di default per tutte le utenze e attivabile/disattivabile dalla sezione ''Pagine di discussione'' nelle [[Speciale:Preferenze#mw-prefsection-editing|preferenze sulla casella di modifica]] e dalla sezione ''Strumenti di discussione'' delle [[Speciale:Preferenze#mw-prefsection-betafeatures|funzionalità beta]], è possibile: * Rispondere a dei messaggi premendo il pulsante <code>'''[rispondi]'''</code> posto in seguito ad ogni messaggio; nel caso il messaggio non sia firmato, il pulsante non apparirà e dunque sarà necessario procedere con la [[#Modifica manuale (desktop)|modifica manuale]]. * Creare nuove discussioni premendo la linguetta "'''Aggiungi discussione'''" o aprendo una pagina di discussione non esistente. * Ricevere una notifica ogni volta che viene aggiunto un messaggio in una discussione premendo il pulsante <code>'''[iscriviti]'''</code>, situato all'estremo destro dello schermo, alla stessa altezza del titolo della sezione. ===Modifica manuale (desktop)=== [[File:Itwiki_help_reply_desktop.png|thumb|upright=1.7|right|link=|Esempio di risposta attraverso la modifica manuale]] [[File:Itwiki help newsection desktop.png|thumb|upright=1.7|left|link=|Dimostrazione di come si aggiunge una nuova discussione]] {{Avviso |testo = '''Lo sapevi che''' su Wikipedia esiste un [[Aiuto:Tour guidato|Tour guidato]] per imparare in fretta ad usare le pagine di discussione? '''[[Aiuto:Tour_guidato/Tutorial_2-1|Clicca qui per provare!]]''' |tipo = contenuto |immagine = [[File:Bombilla amarilla - yellow Edison lamp.svg|40px|!]] }} Per partecipare ad una discussione utilizzando il '''metodo manuale''', bisogna [[Aiuto:Modifica|modificare]] la pagina di discussione. Ciò avviene in modo identico ad una pagina tradizionale, attraverso la linguetta "'''{{MediaWiki:Visualeditor-ca-editsource}}'''" posta nella parte alta della schermata, oppure utilizzando il pulsante "<code>[modifica]</code>" posto fianco del titolo di una delle sezioni che compongono la pagina. Si può iniziare una nuova discussione premendo la linguetta "'''Aggiungi discussione'''" o aprendo una pagina di discussione non esistente. Per facilitare la lettura della pagina, si raccomanda di usare i seguenti accorgimenti: * Lasciare sempre una riga orizzontale vuota tra un messaggio e l'altro; * [[Indentazione|indentare]] ogni nuovo messaggio verso destra, inserendo uno o più segni di "<code>:</code>" (due punti) ''all'inizio del paragrafo''. (vedi la sezione [[#Indentazione della discussione]] qui sotto). '''Chiunque''' scriva in una pagina di discussione '''deve sempre firmare''' il proprio messaggio prima di salvare la pagina. {{vedi anche|Aiuto:Firma}} Solo quando si risponde utilizzando il metodo manuale è opportuno inserire manualmente la firma. [[File:Per firmare.png|thumb|upright=2.7|Clicca sempre '''questo bottone''' quando hai finito di scrivere il tuo intervento e prima di salvare la pagina.]] Ci sono due modi per scrivere la propria firma: * Finito di scrivere, clicca sul pulsante raffigurante una penna, posizionato in alto nella finestra di modifica. * Finito di scrivere, aggiungi manualmente <code><nowiki>"--~~~~"</nowiki></code> alla fine del tuo messaggio. Dopo aver salvato la pagina, al posto di <nowiki>"--~~~~"</nowiki> compariranno il nome utente di chi ha scritto il messaggio, insieme alla data e all'ora di salvataggio della pagina. Se non sei un utente registrato, non hai effettuato il [[Aiuto:Login|login]] o la tua sessione non è più attiva, al posto del nome utente comparirà il tuo indirizzo IP. Per verificare di essere connessi con la propria utenza basta premere il pulsante [[Aiuto:Anteprima|anteprima]] della pagina prima di salvarla e verificare che al posto della firma <nowiki>"~~~~"</nowiki> appaia il proprio nome utente. {{Avviso |tipo = importante |immagine = [[File:Emblem-important-red.svg|40px|Smile]] | testo = Usa la firma sempre soltanto nelle pagine di discussione, '''MAI''' nelle voci di Wikipedia. ''(Le voci sono prodotti comunitari, non vanno mai firmate!)'' }} ===Mobile=== [[File:Itwiki_help_reply_mobile.jpg|thumb|upright=1|right|link=|Esempio di risposta da mobile]] Per partecipare ad una discussione utilizzando '''dispositivi mobili''', bisogna aprire la pagina di discussione, premere sul titolo della sezione interessata e scrivere la risposta nel box in fondo alla pagina. Si può iniziare una nuova discussione premendo il pulsante "'''Aggiungi argomento'''" posto in cima alla pagina di discussione o aprendo una pagina di discussione non esistente. Per facilitare la lettura della pagina, si raccomanda di [[Indentazione|indentare]] ogni nuovo messaggio verso destra, inserendo uno o più segni di "<code>:</code>" (due punti) ''all'inizio del paragrafo''. (vedi la sezione [[#Indentazione della discussione]] qui sotto). Oppure selezionando l'opzione "Leggi come pagina Wiki" è possibile, cliccando sul simbolo della matita '''[[File:OOjs_UI_icon_edit-ltr.svg|link=|alt={{int|vector-view-edit|lang=it}}]]''', aggiungere il commento come spiegato in [[#Modifica manuale (desktop)|modifica manuale]]. Se non si accede a Wikipedia dal web ma dall'applicazione per dispositivi mobili, il pulsante per accedere alle pagine di discussione sarà situato in fondo alle voci. </div> '' [ommiss] '' CC-BY-SA 4.0 [[:it:Aiuto:Pagina di discussione]] 8odmsjg8xepbgjagk2esoi1a10zub42 Wikipedia:Village pump/topic list 4 146208 760331 759449 2026-09-04T21:08:17Z Cewbot 33876 [[User:Cewbot/log/20170915/configuration|Generate topic list: 13 topics]] 760331 wikitext text/x-wiki <!-- This page is auto-generated by bot. Please contact the bot operator to improve the tool. --> {| class="wikitable sortable mw-collapsible" style="float:left;" |- ! data-sort-type="number" style="font-weight: normal;" | <small>#</small> !! 💭 Title !! <span title="Count of comments">💬</span> !! <span title="Count of peoples in discussion">👥</span> !! 🙋 Last editor !! data-sort-type="isoDate" | <span title="Date/Time">🕒 <small>(UTC)</small></span> |- | style="text-align: right;" | 1 | [[Wikipedia:Village pump#Script|Script]] | style="text-align: right;" | 8 | style="text-align: right;" | 5 | style="background-color: #bbb;" | [[User:LuniZunie|LuniZunie]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2025-11-09T16:47:00.000Z" | 2025-11-09 <span style="color: blue;">16:47</span> |- | style="text-align: right;" | 2 | [[Wikipedia:Village pump#Report_concerning_Tanbiruzzammn|Report concerning Tanbiruzzammn]] | style="text-align: right;" | 2 | style="text-align: right;" | 2 | style="background-color: #bbb;" | [[User:Barras|Barras]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2025-12-09T21:45:00.000Z" | 2025-12-09 <span style="color: blue;">21:45</span> |- | style="text-align: right;" | 3 | [[Wikipedia:Village pump#Report_concerning_Bucheon|Report concerning Bucheon]] | style="text-align: right;background-color: #fcc;" | 1 | style="text-align: right;background-color: #fcc;" | 1 | style="background-color: #bbb;" | [[User:PieWriter|PieWriter]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-02-19T10:25:00.000Z" | 2026-02-19 <span style="color: blue;">10:25</span> |- | style="text-align: right;" | 4 | [[Wikipedia:Village pump#Versions_and_dates|Versions and dates]] | style="text-align: right;" | 2 | style="text-align: right;background-color: #fcc;" | 1 | style="background-color: #bbb;" | [[Special:Contributions/~2026-13668-13|<span style="color: #c20;">~2026-13668-13</span>]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-03-03T06:17:00.000Z" | 2026-03-03 <span style="color: blue;">06:17</span> |- | style="text-align: right;" | 5 | style="max-width: 24em" | <small>[[Wikipedia:Village pump#Upcoming_Wikimedia_Café_meetup_regarding_the_the_2026-2027_Wikimedia_Foundation_Annual_Plan|Upcoming Wikimedia Café meetup regarding the the 2026-2027 Wikimedia Foundation Annual Plan]]</small> | style="text-align: right;background-color: #fcc;" | 1 | style="text-align: right;background-color: #fcc;" | 1 | style="background-color: #bbb;" | [[User:Pine|Pine]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-03-30T03:46:00.000Z" | 2026-03-30 <span style="color: blue;">03:46</span> |- | style="text-align: right;" | 6 | [[Wikipedia:Village pump#Changes_to_electionadmin_userright|Changes to electionadmin userright]] | style="text-align: right;" | 4 | style="text-align: right;" | 4 | style="background-color: #bbb;" | [[User:Chaotic Enby|Chaotic Enby]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-04-23T15:22:00.000Z" | 2026-04-23 <span style="color: blue;">15:22</span> |- | style="text-align: right;" | 7 | style="max-width: 24em" | <small>[[Wikipedia:Village pump#Report_concerning_Princebarackalibarrydaddyobamaiii|Report concerning Princebarackalibarrydaddyobamaiii]]</small> | style="text-align: right;background-color: #fcc;" | 1 | style="text-align: right;background-color: #fcc;" | 1 | style="background-color: #bbb;" | [[User:MathXplore|MathXplore]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-05-18T12:23:00.000Z" | 2026-05-18 <span style="color: blue;">12:23</span> |- | style="text-align: right;" | 8 | style="max-width: 24em" | <small>[[Wikipedia:Village pump#May_2026_Wikimedia_Café_meetups_regarding_the_Wikimedia_Foundation_Annual_Plan|May 2026 Wikimedia Café meetups regarding the Wikimedia Foundation Annual Plan]]</small> | style="text-align: right;background-color: #fcc;" | 1 | style="text-align: right;background-color: #fcc;" | 1 | style="background-color: #bbb;" | [[User:Pine|Pine]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-05-21T19:54:00.000Z" | 2026-05-21 <span style="color: blue;">19:54</span> |- | style="text-align: right;" | 9 | style="max-width: 24em" | <small>[[Wikipedia:Village pump#June_2026_Wikimedia_Café_meetups_regarding_the_English_Wikipedia_Editor_Reflections_project|June 2026 Wikimedia Café meetups regarding the English Wikipedia Editor Reflections project]]</small> | style="text-align: right;background-color: #fcc;" | 1 | style="text-align: right;background-color: #fcc;" | 1 | style="background-color: #bbb;" | [[User:Pine|Pine]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-06-15T03:56:00.000Z" | 2026-06-15 <span style="color: blue;">03:56</span> |- | style="text-align: right;" | 10 | [[Wikipedia:Village pump#Removal_request|Removal request]] | style="text-align: right;" | 2 | style="text-align: right;" | 2 | style="background-color: #bbb;" | [[User:Vermont|Vermont]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-07-05T20:08:00.000Z" | 2026-07-05 <span style="color: blue;">20:08</span> |- | style="text-align: right;" | 11 | style="max-width: 24em" | <small>[[Wikipedia:Village pump#July_2026_Wikimedia_Café_meetups_regarding_Wikimedia_governance_and_options_for_reform|July 2026 Wikimedia Café meetups regarding Wikimedia governance and options for reform]]</small> | style="text-align: right;background-color: #fcc;" | 1 | style="text-align: right;background-color: #fcc;" | 1 | style="background-color: #bbb;" | [[User:Pine|Pine]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-07-13T03:35:00.000Z" | 2026-07-13 <span style="color: blue;">03:35</span> |- | style="text-align: right;" | 12 | [[Wikipedia:Village pump#Spambot|Spambot]] | style="text-align: right;" | 2 | style="text-align: right;" | 2 | style="background-color: #bbb;" | [[User:BDavis (WMF)|BDavis (WMF)]] | style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-07-23T00:30:00.000Z" | 2026-07-23 <span style="color: blue;">00:30</span> |- | style="text-align: right;" | 13 | style="max-width: 24em" | <small>[[Wikipedia:Village pump#Adding_some_local_content_pages_for_Pretrain_project_testing|Adding some local content pages for Pretrain project testing]]</small> | style="text-align: right;" | 8 | style="text-align: right;" | 4 | style="background-color: #ddd;" | [[User:BDavis (WMF)|BDavis (WMF)]] | style="background-color: #ddd;" data-sort-type="isoDate" data-sort-value="2026-08-28T20:41:00.000Z" | 2026-08-28 <span style="color: blue;">20:41</span> |} {| class="wikitable mw-collapsible mw-collapsed" style="float: left; margin-left: .5em;;{{#if:{{{no_time_legend|}}}|display:none;|}}" ! title="From the latest bot edit" | Legend |- | style="background-color: #efe;" | * In the last hour |- | style="background-color: #eef;" | * In the last day |- | | * In the last week |- | style="background-color: #ddd;" | * In the last month |- | style="background-color: #bbb;" | * More than one month |- ! Manual settings |- | style="max-width: 12em;" | <small>When exceptions occur,<br />please check [[User:Cewbot/log/20170915/configuration|the setting]] first.</small> |- |} {{Clear}} jelyctl7ha1mywt6pfkghcilivw4fxv User:Oshwah/QuickBlockScript.js 2 168690 760243 760189 2026-09-04T12:03:01Z Oshwah 26741 Fixing error message text. 760243 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); let completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; let completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; let api = new mw.Api(); api.postWithToken('edit', { action: 'edit', section: 'new', watchlist: 'unwatch', sectiontitle: monthNames[date.getMonth()] + ' ' + date.getFullYear(), summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])', text: blockTemplate, title: 'User talk:' + targetUser }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } //</nowiki> d1q7kdk86oesfihktt7te20snvbjmfp 760248 760243 2026-09-04T12:07:24Z Oshwah 26741 Adding updated code for function addBlockNoticeToUserTalkPage() for testing. 760248 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } async function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; /*try {*/ const data = await api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }); const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); /*} catch (error) { // If the talk page does not exist, the API call (action=parse) will fail. If this happens, we'll know that the section doesn't exist, and we'll just create it. console.debug('Could not read talk page sections; a new section will be created.',error); }*/ if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } } //</nowiki> 7n4rj7qqieu9vans9rsnc22w1wex28r 760250 760248 2026-09-04T12:09:06Z Oshwah 26741 Update success message. 760250 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } async function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; /*try {*/ const data = await api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }); const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); /*} catch (error) { // If the talk page does not exist, the API call (action=parse) will fail. If this happens, we'll know that the section doesn't exist, and we'll just create it. console.debug('Could not read talk page sections; a new section will be created.',error); }*/ if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } } //</nowiki> eyygj05yn0krifivw0y1vo36hs7y0pq 760255 760250 2026-09-04T12:16:35Z Oshwah 26741 Updating API call functions to be asynchronous and await completion. 760255 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } async function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } await api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } async function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } await api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } async function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; /*try {*/ const data = await api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }); const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); /*} catch (error) { // If the talk page does not exist, the API call (action=parse) will fail. If this happens, we'll know that the section doesn't exist, and we'll just create it. console.debug('Could not read talk page sections; a new section will be created.',error); }*/ if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } } //</nowiki> 19bl9q5id6uf1gxxocsokxsk0wit9cr 760266 760255 2026-09-04T12:22:10Z Oshwah 26741 rm newline. 760266 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } async function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } await api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } async function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } await api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } async function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; /*try {*/ const data = await api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }); const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); /*} catch (error) { // If the talk page does not exist, the API call (action=parse) will fail. If this happens, we'll know that the section doesn't exist, and we'll just create it. console.debug('Could not read talk page sections; a new section will be created.',error); }*/ if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } } //</nowiki> 6y8xeo4mwkzsm8q7syujv7nxud9s9z6 760267 760266 2026-09-04T12:25:24Z Oshwah 26741 Remove try and catch. 760267 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } async function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } await api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } async function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } await api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } async function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; const data = await api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }); const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } } //</nowiki> 53lguucajst55y4bi6uewsumpa2tspi 760268 760267 2026-09-04T12:29:05Z Oshwah 26741 Adding done function to initial API call to get sections. 760268 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } async function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } await api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } async function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } await api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } async function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; const data = await api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).done(function() { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> h0cex4b43soczqyfc04t33xbqsbj695 760269 760268 2026-09-04T12:32:22Z Oshwah 26741 Change .done to .finally. 760269 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } async function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } await api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } async function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } await api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } async function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; const data = await api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).finally(function() { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> hbve0h8xl13ymsota1pt2n8q1xm0rru 760270 760269 2026-09-04T12:36:14Z Oshwah 26741 Remove async and awaits. 760270 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; const data = api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).finally(function() { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> pevq7shp7srz19x9bs0kb18jbca0jxu 760271 760270 2026-09-04T12:37:59Z Oshwah 26741 Testing replacing .finally with .catch. 760271 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; const data = api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).catch(function() { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> byxblvrz8yyv311fvlplho7j7icoq9i 760273 760271 2026-09-04T12:43:45Z Oshwah 26741 Change .catch to .then. 760273 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; const data = api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).then(function() { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> 6xgeq8ywviz3t8iv3n3bbvjtsd6nsbr 760274 760273 2026-09-04T12:47:31Z Oshwah 26741 Fix code. 760274 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).then(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> thkp2svppof6r7vp25ocahbxsvxfwo5 760275 760274 2026-09-04T12:53:02Z Oshwah 26741 Trying .always() 760275 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('block', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('delete', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> n504adwlb9affta493fyx5egr7lgfx6 760299 760275 2026-09-04T18:31:29Z Oshwah 26741 Migrating updated code from test prod. 760299 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('csrf', data ).done(function() { $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(' + blockReason + ')</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('csrf', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> ahzbntstzh1kvuyknug6bbz51f9o1r4 760302 760299 2026-09-04T18:37:09Z Oshwah 26741 Testing nowiki tag encapsulation. 760302 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('csrf', data ).done(function() { $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<nowiki>' + blockReason + '</nowiki>)</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('csrf', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> dat4znk3sm1e295rkbr5fysr9637bbp 760304 760302 2026-09-04T18:42:22Z Oshwah 26741 Change comment angle brackets in block reasons to the escaped HTML char. Removed nowiki encapsulation from success block status. 760304 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Sock puppetry]]. --&gt;','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Sock puppetry]]. --&gt;','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Abusing multiple accounts]]. --&gt;','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} &lt;!-- Username violation, soft block --&gt;','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} &lt;!-- Username violation, hard block --&gt;','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} &lt;!-- Username violation, hard block --&gt;','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} &lt;!-- Username violation, vandalism-only account --&gt;','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} &lt;!-- Promotional username, promotional edits --&gt;','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} &lt;!-- [[WP:SPAMBOT|Spambot]]. --&gt;','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] &lt;!-- Salebot1. --&gt;','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('csrf', data ).done(function() { $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(' + blockReason + ')</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('csrf', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> fxvcmaj4xzzcae9pjbtx320vy5qtgl8 760306 760304 2026-09-04T18:44:53Z Oshwah 26741 TEST. 760306 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Sock puppetry]]. --&gt;','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Sock puppetry]]. --&gt;','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Abusing multiple accounts]]. --&gt;','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} \<!-- Username violation, soft block --&gt;','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} &lt;!-- Username violation, hard block --&gt;','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} &lt;!-- Username violation, hard block --&gt;','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} &lt;!-- Username violation, vandalism-only account --&gt;','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} &lt;!-- Promotional username, promotional edits --&gt;','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} &lt;!-- [[WP:SPAMBOT|Spambot]]. --&gt;','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] &lt;!-- Salebot1. --&gt;','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('csrf', data ).done(function() { $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(' + blockReason + ')</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('csrf', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> 2ybdgqibiwkkgcmq0p7h6y2ouw4zth9 760308 760306 2026-09-04T18:45:52Z Oshwah 26741 Roll back. 760308 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Sock puppetry]]. --&gt;','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Sock puppetry]]. --&gt;','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} &lt;!-- [[WP:BADSOCK|Abusing multiple accounts]]. --&gt;','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} &lt;!-- Username violation, soft block --&gt;','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} &lt;!-- Username violation, hard block --&gt;','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} &lt;!-- Username violation, hard block --&gt;','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} &lt;!-- Username violation, vandalism-only account --&gt;','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} &lt;!-- Promotional username, promotional edits --&gt;','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} &lt;!-- [[WP:SPAMBOT|Spambot]]. --&gt;','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] &lt;!-- Salebot1. --&gt;','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('csrf', data ).done(function() { $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(' + blockReason + ')</i>.</p>'); //$('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('csrf', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> fxvcmaj4xzzcae9pjbtx320vy5qtgl8 760309 760308 2026-09-04T18:51:09Z Oshwah 26741 Uncomment line 510? 760309 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('csrf', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason">' + blockReason + '</span>)</i>.</p>'); $('<span/>').text(blockReason).appendTo('#blocked-reason'); if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('csrf', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> kx8i3deimb1ejlqzoxfol7uotcwknmf 760311 760309 2026-09-04T18:54:51Z Oshwah 26741 TEST. 760311 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('csrf', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with an expiry time of ' + blockDuration + ' <i>(<span id="blocked-reason"></span>)</i>.</p>'); $('<span/>').text(blockReason).appendTo('#blocked-reason'); //This takes the block reason and appends it as the last child of if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('csrf', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> 3pek8c4c46osq8n2gl3qg1jz8fvwsvm 760314 760311 2026-09-04T19:09:51Z Oshwah 26741 Update code, add comment. 760314 javascript text/javascript //<nowiki> $.when(mw.loader.using(['mediawiki.util']), $.ready).then(init_qbs); function init_qbs() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const g_pageNamespace = mw.config.get('wgNamespaceNumber'); const g_operatorUserName = mw.config.get('wgUserName'); const g_targetUserName = getRelevantUserOrRange(); const g_isUserAuthorized = queryUserAuthorization(); //Skips displaying QuickBlockScript options if not in the user, user talk, main, or special space, if the target username is null, //if you're the target (e.g. you're currently in your own userspace), or if you're not a member of a required group. if([-1,0,2,3].indexOf(g_pageNamespace) > -1 && g_isUserAuthorized && g_targetUserName && g_operatorUserName !== g_targetUserName && !([0,2,3].indexOf(g_pageNamespace) > -1 && ip_pattern.test(g_targetUserName))) { addMoreMenuLinks(); } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function getRelevantUserOrRange() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); let discoveredUserName = mw.util.getParamValue('target') || document.querySelector('#mw-target-user-or-ip')?.value; if (!discoveredUserName && specialPageName === 'Contributions') { const title = mw.config.get('wgTitle'); const prefix = 'Contributions/'; if (title.startsWith(prefix)) { discoveredUserName = title.slice(prefix.length); } } else if (!discoveredUserName) { discoveredUserName = mw.config.get('wgRelevantUserName'); } return discoveredUserName; } function addMoreMenuLinks() { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPUser = ip_pattern.test(targetUserName); //Will only display IP block links to the menu if the user is an IP, vice versa for an account. if (!isIPUser) { addAccountLinks(); } else { addIPLinks(); } } function addAccountLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the operating user is not a CU. const pageNamespace = mw.config.get('wgNamespaceNumber'); //SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (S)', 'ca-qbs-sockblock-s', 'Apply indefinite SOCK BLOCK on account (SILENT)'); $('#ca-qbs-sockblock-s').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); //SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKPUPPET (NT, NE, S)', 'ca-qbs-sockblock-ntnes', 'Apply indefinite SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-sockblock-ntnes').on('click', function() { if (confirm('This will block the account as a SOCK PUPPET and WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-sockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'[[WP:BADSOCK|Sock puppetry]]','uw-sockblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKPUPPET (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (S)', 'ca-qbs-cusockblock-s', 'Apply indefinite CHECKUSER SOCK BLOCK on account (SILENT)'); $('#ca-qbs-cusockblock-s').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); //{{CU}} SOCKPUPPET (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKPUPPET (NT, NE, S)', 'ca-qbs-cusockblock-ntnes', 'Apply indefinite CHECKUSER SOCK BLOCK on account (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cusockblock-ntnes').on('click', function() { if (confirm('This will CHECKUSER block the account as a SOCK PUPPET WITH EMAIL AND TPA REVOKED and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cusockblock-ntnes').text('Please wait...'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Sock puppetry]]. -->','checkuserblock-account','indefinite'); } }); } //SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SOCKMASTER', 'ca-qbs-abusingaccblock', 'Apply INDEFINITE block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-abusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-abusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'[[WP:BADSOCK|Abusing multiple accounts]]','uw-abusingaccblock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SOCKMASTER (ABUSING MULTIPLE ACCOUNTS) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SOCKMASTER', 'ca-qbs-cuabusingaccblock', 'Apply INDEFINITE CHECKUSER block on account for ABUSING MULTIPLE ACCOUNTS'); $('#ca-qbs-cuabusingaccblock').on('click', function() { if (confirm('This will block the account for ABUSING MULTIPLE ACCOUNTS and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuabusingaccblock').text('Please wait...'); prepForBlock(1,1,0,1,'{{checkuserblock-account}} <!-- [[WP:BADSOCK|Abusing multiple accounts]]. -->','uw-abusingaccblock','indefinite'); } }); } //VANDALISM - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H)', 'ca-qbs-36hvandalblock', 'Execute 36 hour VANDALISM BLOCK on account'); $('#ca-qbs-36hvandalblock').on('click', function() { if (confirm('This will block the account for VANDALISM for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','36 hours'); } }); //VANDALISM - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (Indef)', 'ca-qbs-indefvandalblock', 'Apply indefinite VANDALISM BLOCK on account'); $('#ca-qbs-indefvandalblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for VANDALISM.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefvandalblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VAND|Vandalism]]','uw-vblock','indefinite'); } }); //VANDALISM-ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VOA BLOCK', 'ca-qbs-voablock', 'Apply indefinite VOA BLOCK on account'); $('#ca-qbs-voablock').on('click', function() { if (confirm('This will INDEFINITELY block the account as a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-voablock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:VOA|Vandalism-only account]]','uw-voablock','indefinite'); } }); //BLOCK EVASION - ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION', 'ca-qbs-evadeblockaccount', 'Apply indefinite EVASION BLOCK on account'); $('#ca-qbs-evadeblockaccount').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //BLOCK EVASION - ACCOUNT (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (S)', 'ca-qbs-evadeblockaccount-s', 'Apply indefinite EVASION BLOCK on account (SILENT)'); $('#ca-qbs-evadeblockaccount-s').on('click', function() { if (confirm('This will INDEFINITELY block the account for BLOCK EVASION and WITHOUT leaving a talk page block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-evadeblockaccount-s').text('Please wait...'); prepForBlock(1,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-tempevadeblock','indefinite'); } }); //DISRUPTION - Account (36H) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H)', 'ca-qbs-36hdisruptionblock', 'Execute 36 hour BLOCK on account for disruptive editing'); $('#ca-qbs-36hdisruptionblock').on('click', function() { if (confirm('This will block the account for DISRUPTIVE EDITING for 36 HOURS.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-36hdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //DISRUPTION - Account (INDEF) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (Indef)', 'ca-qbs-indefdisruptionblock', 'Execute INDEFINITE BLOCK on account for disruptive editing'); $('#ca-qbs-indefdisruptionblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for DISRUPTIVE EDITING.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-indefdisruptionblock').text('Please wait...'); prepForBlock(1,0,0,1,'[[WP:DE|Disruptive editing]]','uw-disruptblock','indefinite'); } }); //WP:NOTHERE mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'WP:NOTHERE', 'ca-qbs-nothereblock', 'Apply indefinite WP:NOTHERE BLOCK on account'); $('#ca-qbs-nothereblock').on('click', function() { if (confirm('This will INDEFINITELY block the account for WP:NOTHERE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-nothereblock').text('Please wait...'); prepForBlock(1,0,0,1,'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]','uw-nothereblock','indefinite'); } }); //UPOL BLOCK - SOFT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - Soft', 'ca-qbs-upolsoftblock', 'Execute indefnite UPOL SOFT BLOCK on account'); $('#ca-qbs-upolsoftblock').on('click', function() { if (confirm('This will indefinitely soft block the user for UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolsoftblock').text('Please wait...'); prepForBlock(0,0,0,1,'{{uw-ublock}} <!-- Username violation, soft block -->','Uw-ublock','indefinite'); } }); //UPOL BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD', 'ca-qbs-upolhardblock', 'Execute indefnite UPOL HARD BLOCK on account'); $('#ca-qbs-upolhardblock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - HARD (SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - HARD (S)', 'ca-qbs-upolhardblock-s', 'Execute indefnite UPOL HARD BLOCK on account (SILENT)'); $('#ca-qbs-upolhardblock-s').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolhardblock-s').text('Please wait...'); prepForBlock(1,0,0,0,'{{uw-uhblock}} <!-- Username violation, hard block -->','Uw-uhblock','indefinite'); } }); //UPOL BLOCK - VOA mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'UPOL Block - VOA', 'ca-qbs-upolvoablock', 'Apply indefinite UPOL VOA ({{uw-vaublock}}) BLOCK on account'); $('#ca-qbs-upolvoablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the user for blatant UPOL violation AND ALSO a VOA.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-upolvoablock').text('Please wait...'); prepForBlock(1,0,0,1,'{{uw-vaublock}} <!-- Username violation, vandalism-only account -->','uw-vaublock','indefinite'); } }); //IF THE CURRENT PAGE IS A PAGE THAT CAN BE DELETED, EXECUTE THE FUNCTIONS TO SHOW THE OTHER "DELETE AND BLOCK" OPTIONS if([0,2,3].indexOf(pageNamespace) > -1) { //PROMO BLOCK - HARD mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Promo Block - HARD', 'ca-qbs-promohardblock', 'G11 the current page and apply indefinite Promo HARD BLOCK on account'); $('#ca-qbs-promohardblock').on('click', function() { if (confirm('This will G11 the current page and HARD BLOCK the account as a promotional username with promotional edits.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-promohardblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'{{uw-spamublock}} <!-- Promotional username, promotional edits -->','uw-spamublock','indefinite'); } }); } //SPAM / ADVERTISING ONLY ACCOUNT mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'Spam / Ad-only Block', 'ca-qbs-soablock', 'Apply indefinite spam / advertising-only HARD BLOCK on account'); $('#ca-qbs-soablock').on('click', function() { if (confirm('This will INDEFINITELY HARD BLOCK the account as spam / advertising-only.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-soablock').text('Please wait...'); //deleteCurrentPage('G11'); prepForBlock(1,0,0,1,'[[WP:SPAM|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account','uw-soablock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SPAMBOT (NT, NE, S)', 'ca-qbs-spambotblock', 'G11 the current page and apply indefinite SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-spambotblock').on('click', function() { if (confirm('This will delete the page and block the user as a SPAMBOT and revoke email and TPA and leave NO NOTICE.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-spambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'[[WP:SPAMBOT|Spambot]]','uw-spamublock','indefinite'); } }); if (checkuserCheck) { //{{CU}} SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} SPAMBOT (NT, NE, S)', 'ca-qbs-cuspambotblock', 'G11 the current page and apply indefinite CHECKUSER SPAMBOT BLOCK (NO TPA, NO EMAIL, SILENT)'); $('#ca-qbs-cuspambotblock').on('click', function() { if (confirm('This will block the user as a SPAMBOT and AS A CHECKUSER BLOCK and WITHOUT leaving a talk page notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-cuspambotblock').text('Please wait...'); deleteCurrentPage('G11'); prepForBlock(1,1,1,0,'{{checkuserblock-account}} <!-- [[WP:SPAMBOT|Spambot]]. -->','checkuserblock-account','indefinite'); } }); } } //SALEBOT1 LTA BLOCK (NO TPA, NO EMAIL, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'SALEBOT1 LTA (NT, NE, S)', 'ca-qbs-salebot1block', 'Execute indefinite BLOCK on account as a sock of Salebot1'); $('#ca-qbs-salebot1block').on('click', function() { if (confirm('This will block the account and as a sock of Salebot1. NO NOTICE IS LEFT.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('ca-qbs-salebot1block').text('Please wait...'); prepForBlock(1,1,1,0,'Abusing [[WP:Sockpuppetry|multiple accounts]] <!-- Salebot1. -->','uw-sockblock','indefinite'); } }); if([0,2,3].indexOf(pageNamespace) > -1) { //G11 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G11 (No block, S)', 'ca-qbs-g11delete', 'Execute G11 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g11delete').on('click', function() { if (confirm('This will delete the current page under G11 (ADVERTISING / PROMOTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g11delete').text('Please wait...'); deleteCurrentPage('G11'); } }); //G10 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G10 (No block, S)', 'ca-qbs-g10delete', 'Execute G10 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g10delete').on('click', function() { if (confirm('This will delete the current page under G10 (ATTACK PAGE). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g10delete').text('Please wait...'); deleteCurrentPage('G10'); } }); //G5 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G5 (No block, S)', 'ca-qbs-g5delete', 'Execute G5 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g5delete').on('click', function() { if (confirm('This will delete the current page under G5 (CREATED IN VIOLATION OF BLOCK, BAN, OR SANCTION). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g5delete').text('Please wait...'); deleteCurrentPage('G5'); } }); //G3 DELETION mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DELETE PAGE - G3 (No block, S)', 'ca-qbs-g3delete', 'Execute G3 deletion on current page only (no block issued, SILENT)'); $('#ca-qbs-g3delete').on('click', function() { if (confirm('This will delete the current page under G3 (VANDALISM). No block is applied and NO NOTICE is left.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-g3delete').text('Please wait...'); deleteCurrentPage('G3'); } }); } } function addIPLinks() { const checkuserCheck = mw.config.get('wgUserGroups').includes('checkuser'); //Skips displaying checkuser block links to the menu if the user is not a CU. if (checkuserCheck) { //{{CU}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, S)', 'ca-qbs-ip72hcublock-s', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (72H, HARD, S)', 'ca-qbs-ip72hcublock-hbs', 'Execute 72 hour CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcublock-hbs').on('click', function() { if (confirm('This will CHECKUSER HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcublock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{CheckUser block}}','CheckUser block','72 hours'); } }); //{{CU}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU}} (1W, S)', 'ca-qbs-ip1wcublock-s', 'Execute 1 week CHECKUSER BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcublock-s').on('click', function() { if (confirm('This will CHECKUSER BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcublock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{CheckUser block}}','CheckUser block','1 week'); } }); //{{CU-WIDE}} (72H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (72H, S)', 'ca-qbs-ip72hcuwideblock-s', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (72H, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} HARD (72H, HARD, S)', 'ca-qbs-ip72hcuwideblock-hbs', 'Execute 72 hour CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip72hcuwideblock-hbs').on('click', function() { if (confirm('This will CHECKUSER-WIDE HARD BLOCK the IP for 72 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip72hcuwideblock-hbs').text('Please wait...'); prepForBlock(1,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','72 hours'); } }); //{{CU-WIDE}} (1W, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', '{{CU-WIDE}} (1W, S)', 'ca-qbs-ip1wcuwideblock-s', 'Execute 1 week CHECKUSER-WIDE BLOCK on IP (SILENT)'); $('#ca-qbs-ip1wcuwideblock-s').on('click', function() { if (confirm('This will CHECKUSER-WIDE BLOCK the IP for 1 WEEK and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ip1wcuwideblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'{{Checkuserblock-wide}}','Checkuserblock-wide','1 week'); } }); } //VANDALISM (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'VANDALISM (36H, S)', 'ca-qbs-ipvandalblock-s', 'Execute 36 hour VANDALISM BLOCK on IP (SILENT)'); $('#ca-qbs-ipvandalblock-s').on('click', function() { if (confirm('This will block the user for VANDALISM for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipvandalblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Vandalism|Vandalism]]','uw-vblock','36 hours'); } }); //DISRUPTION (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'DISRUPTION (36H, S)', 'ca-qbs-ipdisruptionblock-s', 'Execute 36 hour disruptive editing BLOCK'); $('#ca-qbs-ipdisruptionblock-s').on('click', function() { if (confirm('This will block the IP for DISRUPTIVE EDITING for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipdisruptionblock-s').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:Disruptive editing|Disruptive editing]]','uw-disruptblock','36 hours'); } }); //BLOCK EVASION - IP (36H, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'BLOCK EVASION (36H, S)', 'ca-qbs-ipevadeblock-s', 'Execute 36 hour EVASION BLOCK on IP (SILENT)'); $('#ca-qbs-ipevadeblock-s').on('click', function() { if (confirm('This will block the IP for BLOCK EVASION for 36 HOURS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipevadeblock').text('Please wait...'); prepForBlock(0,0,0,0,'[[WP:EVADE|Block evasion]]','Uw-ipevadeblock','36 hours'); } }); //PROXY OR VPN (3M, HARD, SILENT) mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'PROXY OR VPN (3M, HARD, S)', 'ca-qbs-ipproxyblock-s', 'Execute 3 month PROXY HARD BLOCK on IP (SILENT)'); $('#ca-qbs-ipproxyblock-s').on('click', function() { if (confirm('This will HARD BLOCK the IP as a PROXY for 3 MONTHS and WITHOUT leaving a block notice.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { $('#ca-qbs-ipproxyblock-s').text('Please wait...'); prepForBlock(1,1,0,0,'{{blocked proxy}}','uw-proxyblock','3 months'); } }); } function prepForBlock(p_isHardBlock, p_overwriteExistingBlock, p_revokeTalkAndEmail, p_addTalkPageNotice, p_blockReason, p_blockTemplateName, p_blockDuration) { const blockDurationParameter = ((p_blockDuration && (p_blockDuration == 'indefinite' || p_blockDuration == 'infinite')) || !p_blockDuration) ? 'indef=yes' : ('time=' + p_blockDuration); const blockTemplateSyntax = p_blockTemplateName ? '{{subst:' + p_blockTemplateName + '|' + blockDurationParameter + '|sig=yes}}' : null; const blockDuration = (!p_blockDuration ? 'indefinite' : p_blockDuration); const addTalkPageNotice = (!blockTemplateSyntax ? 0 : p_addTalkPageNotice); applyBlockToUser(p_isHardBlock,p_overwriteExistingBlock,blockDuration,p_revokeTalkAndEmail,addTalkPageNotice,p_blockReason,p_blockTemplateName,blockTemplateSyntax); } function applyBlockToUser(isHardBlock, overwriteExistingBlock, blockDuration, revokeTalkAndEmail, addTalkPageNotice, blockReason, blockTemplateName, blockTemplateSyntax) { //We must define the pattern for an IP address and CIDR range and check ourselves, as the built-in mw.util.isIPAddress() function returns false for IP CIDR ranges. const ip_pattern = /^(?:(?:(?:1?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:1?\d?\d|2[0-4]\d|25[0-5])|(?:[\da-f]{0,4}:){1,7}[\da-f]{0,4})(?:\/\d{1,2})?$/i; const targetUserName = getRelevantUserOrRange(); const isIPBlock = ip_pattern.test(targetUserName); const href = '<a href="' + mw.util.getUrl('Special:Contributions/' + targetUserName) + '">' + targetUserName + '</a>'; let api = new mw.Api(); let data = { action: 'block', expiry: blockDuration, reason: blockReason, watchuser: false, user: targetUserName } // These block options apply if these parameters are passed via the API at all, regardless of value. We have to amend them after declaration. if (overwriteExistingBlock) { data.reblock = 1; } if (!isIPBlock) { // Block is being applied to an account. if (isHardBlock) { // Account HARD BLOCK. data.autoblock = 1; // Enable autoblock. data.nocreate = 1; // Enable blocking account creation. if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } else { // Account soft block. data.allowusertalk = 1; // Allow user talk page access. } } else { // Block is being applied to an IP. data.nocreate = 1; // Enable blocking account creation. if (!isHardBlock) { data.anononly = 1; } if (revokeTalkAndEmail) { data.noemail = 1; // Disable email access. } else { data.allowusertalk = 1; // Allow user talk page access. } } api.postWithToken('csrf', data ).done(function() { const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; $('#mw-content-text').append('<p><b>Success</b> blocking '+ href + ' with a duration of ' + blockDuration + ' <i>(<span id="blocked-reason"></span>)</i>.</p>'); //Adds empty span 'block-reason' (see next line). $('<span/>').text(blockReason).appendTo('#blocked-reason'); //This takes the block reason as literal text and appends it to span object 'blocked-reason', so that the comments in block reasons display as text. if (addTalkPageNotice) { addBlockNoticeToUserTalkPage(blockTemplateName,blockTemplateSyntax); } else { $('#mw-content-text').append(completeMsg); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> blocking ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Blocking ' + targetUserName + ' FAILED. Error details: ' + error + '.'); }); } function deleteCurrentPage(str) { const deletionReason_G11 = '[[WP:G11|G11]]: Unambiguous [[Wikipedia:NOTADVERTISING|advertising]] or promotion'; const deletionReason_G10 = '[[WP:G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]'; const deletionReason_G5 = '[[WP:G5|G5]]: Creation by a [[WP:BAN|banned]] or [[WP:BLOCK|blocked]] user in violation of ban or block'; const deletionReason_G3 = '[[WP:G3|G3]]: Pure [[WP:VAND|vandalism]] or blatant [[WP:HOAX|hoax]]'; const pageName = mw.config.get('wgPageName'); const href = '<a href="' + mw.util.getUrl(pageName) + '">' + pageName + '</a>'; let api = new mw.Api(); let reason; if (str) { switch (str) { case 'G11': reason = deletionReason_G11; break; case 'G10': reason = deletionReason_G10; break; case 'G5': reason = deletionReason_G5; break; case 'G3': reason = deletionReason_G3; break; default: $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } } else { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>\'str\' passed to function deleteCurrentPage() is unrecognized or malformed</i>.</p>'); console.warn('Error (QuickBlockScript): \'str\' passed to function deleteCurrentPage() is unrecognized or malformed.'); return; } api.postWithToken('csrf', { action: 'delete', reason: reason, watchlist: false, title: pageName }).done(function() { $('#mw-content-text').append('<p><b>Success</b> deleting page ' + href + '<i>('+ reason + ')</i>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> deleting page ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Deleting page ' + pageName + ' FAILED. Error details: ' + error + '.'); }); } function addBlockNoticeToUserTalkPage(blockTemplateName, blockTemplate) { const targetUser = getRelevantUserOrRange(); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUser) + '">User talk:' + targetUser + '</a>'; const completeMsg = '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const completeWithErrorsMsg = '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'; const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const date = new Date(); const sectionTitle = monthNames[date.getMonth()] + ' ' + date.getFullYear(); const api = new mw.Api(); let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUser, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line === sectionTitle); if (existingSection) { // A section with the title [current month and year] already exists. Append the notification to the end of that section. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: existingSection.index, appendtext: '\n\n' + blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Appended template {{' + blockTemplateName + '}} to existing section.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUser, watchlist: 'unwatch', section: 'new', sectiontitle: sectionTitle, text: blockTemplate, summary: 'Adding block notice ({{' + blockTemplateName + '}}) to user talk page (using [[User:Oshwah/QuickBlockScript|QBS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + '. Created new section with template {{' + blockTemplateName + '}}.</p>'); $('#mw-content-text').append(completeMsg); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Adding block notice to User talk:' + targetUser + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append(completeWithErrorsMsg); }); } }); } //</nowiki> 56j8e4dg49j6a6m655yvexrekh324dr User:Cryptocurrency777/sandbox2 2 175572 760282 743629 2026-09-04T13:24:49Z Cryptocurrency777 73698 760282 wikitext text/x-wiki {{Infobox song|Name=Cameras On|Artist=[[Anti-Light]]|Released=July 17, 2025|Format=Digital Download|Recorded=2025|Genre=EDM, Hyperpop|Language=English|Length=1:37|Label=Angel Asylum|Producer=Pröz|Chronology=|prev=Deadangel|next=Oh My|prev_no=2025|track_no=2025|next_no=2025}} '''Cameras On''' (stylized in all caps) is a single released by Danish singer and producer [[Anti-Light]] on July 17, 2025 under the Angel Asylum Label. Like several of Anti-Light's other songs, it was produced by French producer Pröz. == Background == Cameras On was first announced as a surprise release in July 2025. It has a Beats Per Minute of 130.<ref>{{Citation | url=https://tunebat.com/Info/CAMERAS-ON-Anti-Light-Pr-z/5nadas5PjZcEgrzGRCg1wV|title=CAMERAS-ON-Anti-Light}}</ref> By the time of its release, it became Anti-Light's shortest release in length. == Music Video == The video is a visualizer made by [[User:Cryptocurrency777/sandbox13|Eris]] using the cover art of the song, which was drawn by @00moonsu on [[Instagram]], which zooms in and out twice each "interval". ==Track listing== {{Track listing | headline = ''Cameras On (Angel Asylum Mix)'' track listing | total_length = 3:13 | title1 = Cameras On | note1 = sped up | length1 = 1:25 | title2 = Cameras On | note2 = slowed | length2 = 1:48 }} ==References== {{reflist}} 64hl7yubghlwmp49nt0ucmtfyn9la2j User:Oshwah/WelcomeNewStudent.js 2 176931 760316 760169 2026-09-04T19:48:06Z Oshwah 26741 Migrate code updates and beta code. 760316 javascript text/javascript //<nowiki> var wns_option_enableExtendedFeatures; $.when(mw.loader.using(['mediawiki.util']), $.ready).done(init); function init() { const myUsername = mw.config.get('wgUserName'); //Script operator's username. const targetUsername = mw.config.get('wgRelevantUserName'); //Target account's username. const pageNamespace = mw.config.get('wgNamespaceNumber'); //The namespace number of the current page. const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Name of the current special page. If we're not on a special page, this returns false. //Only run this script if we're currently viewing another registered account's user space, user talk space, or Special:Contributions page. if([-1,2,3].indexOf(pageNamespace) !== -1 && (!specialPageName || specialPageName == 'Contributions') && targetUsername && myUsername != targetUsername && targetUsername.substring(0, 2) !== '~2' && !mw.util.isIPAddress(targetUsername)) { //If the script operator does not have 'wns_option_enableExtendedFeatures = true;' added to their common.js page, set the extended features flag to false by default. const enableExtendedFeatures = (typeof wns_option_enableExtendedFeatures == 'undefined')?(false):(wns_option_enableExtendedFeatures); const userAuthorized = queryUserAuthorization(); const api = new mw.Api(); const pagesList = [ 'User:' + targetUsername, 'User talk:' + targetUsername, 'User:' + targetUsername + '/sandbox', 'User:' + targetUsername + '/be bold', 'User:' + targetUsername + '/Evaluate an Article', 'User:' + targetUsername + '/Choose an Article', 'User talk:' + targetUsername + '/talk page tutorial' ]; //Insert this link above the first QuickBlockScript menu item ('ca-qbs-sockblock-s') - see User:Oshwah/QuickBlockScript.js. //The function falls back to inserting the link at the end of the portlet if 'ca-qbs-sockblock-s' doesn't exist. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student','ca-welcome-wikied','Activate WelcomeNewStudent script',null,'#ca-qbs-sockblock-s'); $('#ca-welcome-wikied').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome student template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,0); } }); if (enableExtendedFeatures) { const prevNode = userAuthorized ? '#ca-welcome-wikied-s' : '#ca-welcome-wikied'; if (userAuthorized) { //Don't display this link for users who are not new page reviewers or administrators, since the silent function only patrols pages. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (S)','ca-welcome-wikied-s','Activate WelcomeNewStudent script (SKIP welcome message)',null,$('#ca-welcome-wikied').next()); $('#ca-welcome-wikied-s').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled but SKIP adding a welcome template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,1,0,0); } }); } mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (AUTO)','ca-welcome-wikied-a','Activate WelcomeNewStudent script (for instructor)',null,$(prevNode).next()); $('#ca-welcome-wikied-a').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and detect if a welcome template already exists, and adds one if none is detected (EXPERIMENTAL).\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,1,1); } }); mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new instructor','ca-welcome-wikied-i','Activate WelcomeNewStudent script (for instructor)',null,$('#ca-welcome-wikied-a').next()); $('#ca-welcome-wikied-i').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome teacher template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,1); } }); } } } function queryUserAuthorization() { const newPageReviewerCheck = mw.config.get('wgUserGroups').includes('patroller'); const sysopCheck = mw.config.get('wgUserGroups').includes('sysop'); let isAuthorized = newPageReviewerCheck || sysopCheck ? true : false; if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const allowedGlobalGroups = ['steward']; const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { //Check if the user is a member of any of the required global groups. isAuthorized = true; } } else { throw new Error('\'data.query\' does not exist.'); } }).catch(function(error) { console.warn('Error (WelcomeNewStudent): API call to retrieve global group memberships failed. Error: ' + error + '.'); return false; }); } catch (error) { console.warn('Error (WelcomeNewStudent): Parsing API call data for global group memberships failed. Error: ' + error + '.'); return false; } } return isAuthorized; } function getLatestRevisionID(api,title) { let revisionID = null; return api.get({ action: 'query', prop: 'revisions', titles: title, rvprop: 'ids', rvlimit: 1, rvdir: 'newer', format: 'json' }).then(function(data) { let page = Object.values(data.query.pages)[0]; if (page.missing !== undefined) { //The current page doesn't exist if page.missing is set to a value. revisionID = null; } else { revisionID = page.revisions[0].revid; } return revisionID; }); } function patrolPage(api,title) { return getLatestRevisionID(api,title) .then(function(revid) { $('#mw-content-text').append('<p>Marking page <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled...</p>'); if (!revid) {//If null is returned from the function, the current page doesn't exist. Append an error message stating this, then end this function. $('#mw-content-text').append('<p><b>Cannot mark</b> <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Page does not exist.</p>'); return; } return api.postWithToken('csrf', { action: 'patrol', revid: revid, format: 'json' }).then(function() { $('#mw-content-text').append('<p><b>Success</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Error message: ' + error + '. RevID: ' + revid + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not mark page ' + title + ' as patrolled. Error message: ' + error + '. RevID: ' + revid + '.'); }); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> retrieving latest revision from <a href="' + mw.util.getUrl(title) + '">' + title + '</a>. Error message: ' + error + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not retrieve latest revision ID from page ' + title + '. Error message: ' + error + '.'); }); } function addWelcomeMessage(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const talkPage = 'User talk:' + targetUsername; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; api.postWithToken('csrf', { action: 'edit', title: talkPage, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).then(function() { $('#mw-content-text').append( '<p><b>Success</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>: Created new section with ' + templateText + '.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); }).catch(function(error) { $('#mw-content-text').append( '<p><b>Error</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>. Error message: ' + error + '.</p>' + '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); console.warn('Error (WelcomeNewStudent): Could not edit User talk:' + targetUsername + '. Error message: ' + error + '.'); }); } function addWelcomeMessageAuto(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>'; //const talkPage = 'User talk:' + targetUsername; const sectionTitle = 'Welcome'; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUsername, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line.contains(sectionTitle)); if (existingSection) { // A section with the title containing 'Welcome' already exists. Skip adding welcome message. $('#mw-content-text').append('<p><b>Skipped</b> editing '+ href +' and leaving welcome message (WELCOME MESSAGE DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUsername, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + ': Created new section with ' + templateText + '.</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (WelcomeNewStudent): Adding welcome message to User talk:' + targetUsername + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append('<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }); } }); } function skipWelcomeMessage() { const targetUsername = mw.config.get('wgRelevantUserName'); $('#mw-content-text').append( '<p><b>Skipped</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a> and leaving welcome message.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); } function runScript(auth,pages,api,isSilent,isAuto,isInstructor) { $('#ca-welcome-wikied').text('Please wait...'); $('#ca-welcome-wikied-s').text('Please wait...'); $('#ca-welcome-wikied-i').text('Please wait...'); if (auth) { Promise.allSettled( pages.map(function(page) { return patrolPage(api,page); }) ).then(function() { if (!isAuto) { if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } else { addWelcomeMessageAuto(isInstructor,api); } }); } else { $('#mw-content-text').append('<p><b>Skipped</b> searching for and marking common WikiEd user pages as patrolled. <b>Missing required user permissions.</b></p>'); if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } } //</nowiki> g6qe643h4rlnwveuktglct8eyuno5lk 760317 760316 2026-09-04T19:51:10Z Oshwah 26741 Change 'csrf' back to 'patrol'. 760317 javascript text/javascript //<nowiki> var wns_option_enableExtendedFeatures; $.when(mw.loader.using(['mediawiki.util']), $.ready).done(init); function init() { const myUsername = mw.config.get('wgUserName'); //Script operator's username. const targetUsername = mw.config.get('wgRelevantUserName'); //Target account's username. const pageNamespace = mw.config.get('wgNamespaceNumber'); //The namespace number of the current page. const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Name of the current special page. If we're not on a special page, this returns false. //Only run this script if we're currently viewing another registered account's user space, user talk space, or Special:Contributions page. if([-1,2,3].indexOf(pageNamespace) !== -1 && (!specialPageName || specialPageName == 'Contributions') && targetUsername && myUsername != targetUsername && targetUsername.substring(0, 2) !== '~2' && !mw.util.isIPAddress(targetUsername)) { //If the script operator does not have 'wns_option_enableExtendedFeatures = true;' added to their common.js page, set the extended features flag to false by default. const enableExtendedFeatures = (typeof wns_option_enableExtendedFeatures == 'undefined')?(false):(wns_option_enableExtendedFeatures); const userAuthorized = queryUserAuthorization(); const api = new mw.Api(); const pagesList = [ 'User:' + targetUsername, 'User talk:' + targetUsername, 'User:' + targetUsername + '/sandbox', 'User:' + targetUsername + '/be bold', 'User:' + targetUsername + '/Evaluate an Article', 'User:' + targetUsername + '/Choose an Article', 'User talk:' + targetUsername + '/talk page tutorial' ]; //Insert this link above the first QuickBlockScript menu item ('ca-qbs-sockblock-s') - see User:Oshwah/QuickBlockScript.js. //The function falls back to inserting the link at the end of the portlet if 'ca-qbs-sockblock-s' doesn't exist. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student','ca-welcome-wikied','Activate WelcomeNewStudent script',null,'#ca-qbs-sockblock-s'); $('#ca-welcome-wikied').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome student template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,0); } }); if (enableExtendedFeatures) { const prevNode = userAuthorized ? '#ca-welcome-wikied-s' : '#ca-welcome-wikied'; if (userAuthorized) { //Don't display this link for users who are not new page reviewers or administrators, since the silent function only patrols pages. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (S)','ca-welcome-wikied-s','Activate WelcomeNewStudent script (SKIP welcome message)',null,$('#ca-welcome-wikied').next()); $('#ca-welcome-wikied-s').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled but SKIP adding a welcome template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,1,0,0); } }); } mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (AUTO)','ca-welcome-wikied-a','Activate WelcomeNewStudent script (for instructor)',null,$(prevNode).next()); $('#ca-welcome-wikied-a').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and detect if a welcome template already exists, and adds one if none is detected (EXPERIMENTAL).\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,1,1); } }); mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new instructor','ca-welcome-wikied-i','Activate WelcomeNewStudent script (for instructor)',null,$('#ca-welcome-wikied-a').next()); $('#ca-welcome-wikied-i').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome teacher template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,1); } }); } } } function queryUserAuthorization() { const newPageReviewerCheck = mw.config.get('wgUserGroups').includes('patroller'); const sysopCheck = mw.config.get('wgUserGroups').includes('sysop'); let isAuthorized = newPageReviewerCheck || sysopCheck ? true : false; if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const allowedGlobalGroups = ['steward']; const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { //Check if the user is a member of any of the required global groups. isAuthorized = true; } } else { throw new Error('\'data.query\' does not exist.'); } }).catch(function(error) { console.warn('Error (WelcomeNewStudent): API call to retrieve global group memberships failed. Error: ' + error + '.'); return false; }); } catch (error) { console.warn('Error (WelcomeNewStudent): Parsing API call data for global group memberships failed. Error: ' + error + '.'); return false; } } return isAuthorized; } function getLatestRevisionID(api,title) { let revisionID = null; return api.get({ action: 'query', prop: 'revisions', titles: title, rvprop: 'ids', rvlimit: 1, rvdir: 'newer', format: 'json' }).then(function(data) { let page = Object.values(data.query.pages)[0]; if (page.missing !== undefined) { //The current page doesn't exist if page.missing is set to a value. revisionID = null; } else { revisionID = page.revisions[0].revid; } return revisionID; }); } function patrolPage(api,title) { return getLatestRevisionID(api,title) .then(function(revid) { $('#mw-content-text').append('<p>Marking page <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled...</p>'); if (!revid) {//If null is returned from the function, the current page doesn't exist. Append an error message stating this, then end this function. $('#mw-content-text').append('<p><b>Cannot mark</b> <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Page does not exist.</p>'); return; } return api.postWithToken('patrol', { action: 'patrol', revid: revid, format: 'json' }).then(function() { $('#mw-content-text').append('<p><b>Success</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Error message: ' + error + '. RevID: ' + revid + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not mark page ' + title + ' as patrolled. Error message: ' + error + '. RevID: ' + revid + '.'); }); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> retrieving latest revision from <a href="' + mw.util.getUrl(title) + '">' + title + '</a>. Error message: ' + error + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not retrieve latest revision ID from page ' + title + '. Error message: ' + error + '.'); }); } function addWelcomeMessage(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const talkPage = 'User talk:' + targetUsername; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; api.postWithToken('csrf', { action: 'edit', title: talkPage, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).then(function() { $('#mw-content-text').append( '<p><b>Success</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>: Created new section with ' + templateText + '.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); }).catch(function(error) { $('#mw-content-text').append( '<p><b>Error</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>. Error message: ' + error + '.</p>' + '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); console.warn('Error (WelcomeNewStudent): Could not edit User talk:' + targetUsername + '. Error message: ' + error + '.'); }); } function addWelcomeMessageAuto(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>'; //const talkPage = 'User talk:' + targetUsername; const sectionTitle = 'Welcome'; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUsername, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line.contains(sectionTitle)); if (existingSection) { // A section with the title containing 'Welcome' already exists. Skip adding welcome message. $('#mw-content-text').append('<p><b>Skipped</b> editing '+ href +' and leaving welcome message (WELCOME MESSAGE DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUsername, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + ': Created new section with ' + templateText + '.</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (WelcomeNewStudent): Adding welcome message to User talk:' + targetUsername + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append('<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }); } }); } function skipWelcomeMessage() { const targetUsername = mw.config.get('wgRelevantUserName'); $('#mw-content-text').append( '<p><b>Skipped</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a> and leaving welcome message.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); } function runScript(auth,pages,api,isSilent,isAuto,isInstructor) { $('#ca-welcome-wikied').text('Please wait...'); $('#ca-welcome-wikied-s').text('Please wait...'); $('#ca-welcome-wikied-i').text('Please wait...'); if (auth) { Promise.allSettled( pages.map(function(page) { return patrolPage(api,page); }) ).then(function() { if (!isAuto) { if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } else { addWelcomeMessageAuto(isInstructor,api); } }); } else { $('#mw-content-text').append('<p><b>Skipped</b> searching for and marking common WikiEd user pages as patrolled. <b>Missing required user permissions.</b></p>'); if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } } //</nowiki> gyfs80kpy99zke6vpglufzjlmgehxxv 760319 760317 2026-09-04T19:53:20Z Oshwah 26741 Fix tooltip. 760319 javascript text/javascript //<nowiki> var wns_option_enableExtendedFeatures; $.when(mw.loader.using(['mediawiki.util']), $.ready).done(init); function init() { const myUsername = mw.config.get('wgUserName'); //Script operator's username. const targetUsername = mw.config.get('wgRelevantUserName'); //Target account's username. const pageNamespace = mw.config.get('wgNamespaceNumber'); //The namespace number of the current page. const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Name of the current special page. If we're not on a special page, this returns false. //Only run this script if we're currently viewing another registered account's user space, user talk space, or Special:Contributions page. if([-1,2,3].indexOf(pageNamespace) !== -1 && (!specialPageName || specialPageName == 'Contributions') && targetUsername && myUsername != targetUsername && targetUsername.substring(0, 2) !== '~2' && !mw.util.isIPAddress(targetUsername)) { //If the script operator does not have 'wns_option_enableExtendedFeatures = true;' added to their common.js page, set the extended features flag to false by default. const enableExtendedFeatures = (typeof wns_option_enableExtendedFeatures == 'undefined')?(false):(wns_option_enableExtendedFeatures); const userAuthorized = queryUserAuthorization(); const api = new mw.Api(); const pagesList = [ 'User:' + targetUsername, 'User talk:' + targetUsername, 'User:' + targetUsername + '/sandbox', 'User:' + targetUsername + '/be bold', 'User:' + targetUsername + '/Evaluate an Article', 'User:' + targetUsername + '/Choose an Article', 'User talk:' + targetUsername + '/talk page tutorial' ]; //Insert this link above the first QuickBlockScript menu item ('ca-qbs-sockblock-s') - see User:Oshwah/QuickBlockScript.js. //The function falls back to inserting the link at the end of the portlet if 'ca-qbs-sockblock-s' doesn't exist. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student','ca-welcome-wikied','Activate WelcomeNewStudent script',null,'#ca-qbs-sockblock-s'); $('#ca-welcome-wikied').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome student template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,0); } }); if (enableExtendedFeatures) { const prevNode = userAuthorized ? '#ca-welcome-wikied-s' : '#ca-welcome-wikied'; if (userAuthorized) { //Don't display this link for users who are not new page reviewers or administrators, since the silent function only patrols pages. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (S)','ca-welcome-wikied-s','Activate WelcomeNewStudent script (SKIP welcome message)',null,$('#ca-welcome-wikied').next()); $('#ca-welcome-wikied-s').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled but SKIP adding a welcome template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,1,0,0); } }); } mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (AUTO)','ca-welcome-wikied-a','Activate WelcomeNewStudent script (auto add welcome message)',null,$(prevNode).next()); $('#ca-welcome-wikied-a').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and detect if a welcome template already exists, and adds one if none is detected (EXPERIMENTAL).\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,1,1); } }); mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new instructor','ca-welcome-wikied-i','Activate WelcomeNewStudent script (for instructor)',null,$('#ca-welcome-wikied-a').next()); $('#ca-welcome-wikied-i').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome teacher template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,1); } }); } } } function queryUserAuthorization() { const newPageReviewerCheck = mw.config.get('wgUserGroups').includes('patroller'); const sysopCheck = mw.config.get('wgUserGroups').includes('sysop'); let isAuthorized = newPageReviewerCheck || sysopCheck ? true : false; if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const allowedGlobalGroups = ['steward']; const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { //Check if the user is a member of any of the required global groups. isAuthorized = true; } } else { throw new Error('\'data.query\' does not exist.'); } }).catch(function(error) { console.warn('Error (WelcomeNewStudent): API call to retrieve global group memberships failed. Error: ' + error + '.'); return false; }); } catch (error) { console.warn('Error (WelcomeNewStudent): Parsing API call data for global group memberships failed. Error: ' + error + '.'); return false; } } return isAuthorized; } function getLatestRevisionID(api,title) { let revisionID = null; return api.get({ action: 'query', prop: 'revisions', titles: title, rvprop: 'ids', rvlimit: 1, rvdir: 'newer', format: 'json' }).then(function(data) { let page = Object.values(data.query.pages)[0]; if (page.missing !== undefined) { //The current page doesn't exist if page.missing is set to a value. revisionID = null; } else { revisionID = page.revisions[0].revid; } return revisionID; }); } function patrolPage(api,title) { return getLatestRevisionID(api,title) .then(function(revid) { $('#mw-content-text').append('<p>Marking page <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled...</p>'); if (!revid) {//If null is returned from the function, the current page doesn't exist. Append an error message stating this, then end this function. $('#mw-content-text').append('<p><b>Cannot mark</b> <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Page does not exist.</p>'); return; } return api.postWithToken('patrol', { action: 'patrol', revid: revid, format: 'json' }).then(function() { $('#mw-content-text').append('<p><b>Success</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Error message: ' + error + '. RevID: ' + revid + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not mark page ' + title + ' as patrolled. Error message: ' + error + '. RevID: ' + revid + '.'); }); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> retrieving latest revision from <a href="' + mw.util.getUrl(title) + '">' + title + '</a>. Error message: ' + error + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not retrieve latest revision ID from page ' + title + '. Error message: ' + error + '.'); }); } function addWelcomeMessage(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const talkPage = 'User talk:' + targetUsername; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; api.postWithToken('csrf', { action: 'edit', title: talkPage, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).then(function() { $('#mw-content-text').append( '<p><b>Success</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>: Created new section with ' + templateText + '.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); }).catch(function(error) { $('#mw-content-text').append( '<p><b>Error</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>. Error message: ' + error + '.</p>' + '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); console.warn('Error (WelcomeNewStudent): Could not edit User talk:' + targetUsername + '. Error message: ' + error + '.'); }); } function addWelcomeMessageAuto(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>'; //const talkPage = 'User talk:' + targetUsername; const sectionTitle = 'Welcome'; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUsername, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line.contains(sectionTitle)); if (existingSection) { // A section with the title containing 'Welcome' already exists. Skip adding welcome message. $('#mw-content-text').append('<p><b>Skipped</b> editing '+ href +' and leaving welcome message (WELCOME MESSAGE DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUsername, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + ': Created new section with ' + templateText + '.</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (WelcomeNewStudent): Adding welcome message to User talk:' + targetUsername + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append('<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }); } }); } function skipWelcomeMessage() { const targetUsername = mw.config.get('wgRelevantUserName'); $('#mw-content-text').append( '<p><b>Skipped</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a> and leaving welcome message.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); } function runScript(auth,pages,api,isSilent,isAuto,isInstructor) { $('#ca-welcome-wikied').text('Please wait...'); $('#ca-welcome-wikied-s').text('Please wait...'); $('#ca-welcome-wikied-i').text('Please wait...'); if (auth) { Promise.allSettled( pages.map(function(page) { return patrolPage(api,page); }) ).then(function() { if (!isAuto) { if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } else { addWelcomeMessageAuto(isInstructor,api); } }); } else { $('#mw-content-text').append('<p><b>Skipped</b> searching for and marking common WikiEd user pages as patrolled. <b>Missing required user permissions.</b></p>'); if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } } //</nowiki> 3ji4p5xfiy9w2odz44t7lrggu2o5f3f 760320 760319 2026-09-04T19:59:35Z Oshwah 26741 Fix code; .contains is actually .includes... 760320 javascript text/javascript //<nowiki> var wns_option_enableExtendedFeatures; $.when(mw.loader.using(['mediawiki.util']), $.ready).done(init); function init() { const myUsername = mw.config.get('wgUserName'); //Script operator's username. const targetUsername = mw.config.get('wgRelevantUserName'); //Target account's username. const pageNamespace = mw.config.get('wgNamespaceNumber'); //The namespace number of the current page. const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Name of the current special page. If we're not on a special page, this returns false. //Only run this script if we're currently viewing another registered account's user space, user talk space, or Special:Contributions page. if([-1,2,3].indexOf(pageNamespace) !== -1 && (!specialPageName || specialPageName == 'Contributions') && targetUsername && myUsername != targetUsername && targetUsername.substring(0, 2) !== '~2' && !mw.util.isIPAddress(targetUsername)) { //If the script operator does not have 'wns_option_enableExtendedFeatures = true;' added to their common.js page, set the extended features flag to false by default. const enableExtendedFeatures = (typeof wns_option_enableExtendedFeatures == 'undefined')?(false):(wns_option_enableExtendedFeatures); const userAuthorized = queryUserAuthorization(); const api = new mw.Api(); const pagesList = [ 'User:' + targetUsername, 'User talk:' + targetUsername, 'User:' + targetUsername + '/sandbox', 'User:' + targetUsername + '/be bold', 'User:' + targetUsername + '/Evaluate an Article', 'User:' + targetUsername + '/Choose an Article', 'User talk:' + targetUsername + '/talk page tutorial' ]; //Insert this link above the first QuickBlockScript menu item ('ca-qbs-sockblock-s') - see User:Oshwah/QuickBlockScript.js. //The function falls back to inserting the link at the end of the portlet if 'ca-qbs-sockblock-s' doesn't exist. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student','ca-welcome-wikied','Activate WelcomeNewStudent script',null,'#ca-qbs-sockblock-s'); $('#ca-welcome-wikied').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome student template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,0); } }); if (enableExtendedFeatures) { const prevNode = userAuthorized ? '#ca-welcome-wikied-s' : '#ca-welcome-wikied'; if (userAuthorized) { //Don't display this link for users who are not new page reviewers or administrators, since the silent function only patrols pages. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (S)','ca-welcome-wikied-s','Activate WelcomeNewStudent script (SKIP welcome message)',null,$('#ca-welcome-wikied').next()); $('#ca-welcome-wikied-s').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled but SKIP adding a welcome template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,1,0,0); } }); } mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (AUTO)','ca-welcome-wikied-a','Activate WelcomeNewStudent script (auto add welcome message)',null,$(prevNode).next()); $('#ca-welcome-wikied-a').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and detect if a welcome template already exists, and adds one if none is detected (EXPERIMENTAL).\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,1,1); } }); mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new instructor','ca-welcome-wikied-i','Activate WelcomeNewStudent script (for instructor)',null,$('#ca-welcome-wikied-a').next()); $('#ca-welcome-wikied-i').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome teacher template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,1); } }); } } } function queryUserAuthorization() { const newPageReviewerCheck = mw.config.get('wgUserGroups').includes('patroller'); const sysopCheck = mw.config.get('wgUserGroups').includes('sysop'); let isAuthorized = newPageReviewerCheck || sysopCheck ? true : false; if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const allowedGlobalGroups = ['steward']; const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { //Check if the user is a member of any of the required global groups. isAuthorized = true; } } else { throw new Error('\'data.query\' does not exist.'); } }).catch(function(error) { console.warn('Error (WelcomeNewStudent): API call to retrieve global group memberships failed. Error: ' + error + '.'); return false; }); } catch (error) { console.warn('Error (WelcomeNewStudent): Parsing API call data for global group memberships failed. Error: ' + error + '.'); return false; } } return isAuthorized; } function getLatestRevisionID(api,title) { let revisionID = null; return api.get({ action: 'query', prop: 'revisions', titles: title, rvprop: 'ids', rvlimit: 1, rvdir: 'newer', format: 'json' }).then(function(data) { let page = Object.values(data.query.pages)[0]; if (page.missing !== undefined) { //The current page doesn't exist if page.missing is set to a value. revisionID = null; } else { revisionID = page.revisions[0].revid; } return revisionID; }); } function patrolPage(api,title) { return getLatestRevisionID(api,title) .then(function(revid) { $('#mw-content-text').append('<p>Marking page <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled...</p>'); if (!revid) {//If null is returned from the function, the current page doesn't exist. Append an error message stating this, then end this function. $('#mw-content-text').append('<p><b>Cannot mark</b> <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Page does not exist.</p>'); return; } return api.postWithToken('patrol', { action: 'patrol', revid: revid, format: 'json' }).then(function() { $('#mw-content-text').append('<p><b>Success</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Error message: ' + error + '. RevID: ' + revid + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not mark page ' + title + ' as patrolled. Error message: ' + error + '. RevID: ' + revid + '.'); }); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> retrieving latest revision from <a href="' + mw.util.getUrl(title) + '">' + title + '</a>. Error message: ' + error + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not retrieve latest revision ID from page ' + title + '. Error message: ' + error + '.'); }); } function addWelcomeMessage(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const talkPage = 'User talk:' + targetUsername; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; api.postWithToken('csrf', { action: 'edit', title: talkPage, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).then(function() { $('#mw-content-text').append( '<p><b>Success</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>: Created new section with ' + templateText + '.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); }).catch(function(error) { $('#mw-content-text').append( '<p><b>Error</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>. Error message: ' + error + '.</p>' + '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); console.warn('Error (WelcomeNewStudent): Could not edit User talk:' + targetUsername + '. Error message: ' + error + '.'); }); } function addWelcomeMessageAuto(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>'; //const talkPage = 'User talk:' + targetUsername; const sectionTitle = 'Welcome'; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUsername, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line.includes(sectionTitle)); if (existingSection) { // A section with the title containing 'Welcome' already exists. Skip adding welcome message. $('#mw-content-text').append('<p><b>Skipped</b> editing '+ href +' and leaving welcome message (WELCOME MESSAGE DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { // No matching section exists, so create it. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUsername, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + ': Created new section with ' + templateText + '.</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (WelcomeNewStudent): Adding welcome message to User talk:' + targetUsername + ' FAILED. Error details: ' + error + '.'); $('#mw-content-text').append('<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }); } }); } function skipWelcomeMessage() { const targetUsername = mw.config.get('wgRelevantUserName'); $('#mw-content-text').append( '<p><b>Skipped</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a> and leaving welcome message.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); } function runScript(auth,pages,api,isSilent,isAuto,isInstructor) { $('#ca-welcome-wikied').text('Please wait...'); $('#ca-welcome-wikied-s').text('Please wait...'); $('#ca-welcome-wikied-i').text('Please wait...'); if (auth) { Promise.allSettled( pages.map(function(page) { return patrolPage(api,page); }) ).then(function() { if (!isAuto) { if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } else { addWelcomeMessageAuto(isInstructor,api); } }); } else { $('#mw-content-text').append('<p><b>Skipped</b> searching for and marking common WikiEd user pages as patrolled. <b>Missing required user permissions.</b></p>'); if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } } //</nowiki> onr4yv517tnv04fpgbmpx1c95n287eo 760321 760320 2026-09-04T20:03:23Z Oshwah 26741 Update comments and status messages. 760321 javascript text/javascript //<nowiki> var wns_option_enableExtendedFeatures; $.when(mw.loader.using(['mediawiki.util']), $.ready).done(init); function init() { const myUsername = mw.config.get('wgUserName'); //Script operator's username. const targetUsername = mw.config.get('wgRelevantUserName'); //Target account's username. const pageNamespace = mw.config.get('wgNamespaceNumber'); //The namespace number of the current page. const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Name of the current special page. If we're not on a special page, this returns false. //Only run this script if we're currently viewing another registered account's user space, user talk space, or Special:Contributions page. if([-1,2,3].indexOf(pageNamespace) !== -1 && (!specialPageName || specialPageName == 'Contributions') && targetUsername && myUsername != targetUsername && targetUsername.substring(0, 2) !== '~2' && !mw.util.isIPAddress(targetUsername)) { //If the script operator does not have 'wns_option_enableExtendedFeatures = true;' added to their common.js page, set the extended features flag to false by default. const enableExtendedFeatures = (typeof wns_option_enableExtendedFeatures == 'undefined')?(false):(wns_option_enableExtendedFeatures); const userAuthorized = queryUserAuthorization(); const api = new mw.Api(); const pagesList = [ 'User:' + targetUsername, 'User talk:' + targetUsername, 'User:' + targetUsername + '/sandbox', 'User:' + targetUsername + '/be bold', 'User:' + targetUsername + '/Evaluate an Article', 'User:' + targetUsername + '/Choose an Article', 'User talk:' + targetUsername + '/talk page tutorial' ]; //Insert this link above the first QuickBlockScript menu item ('ca-qbs-sockblock-s') - see User:Oshwah/QuickBlockScript.js. //The function falls back to inserting the link at the end of the portlet if 'ca-qbs-sockblock-s' doesn't exist. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student','ca-welcome-wikied','Activate WelcomeNewStudent script',null,'#ca-qbs-sockblock-s'); $('#ca-welcome-wikied').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome student template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,0); } }); if (enableExtendedFeatures) { const prevNode = userAuthorized ? '#ca-welcome-wikied-s' : '#ca-welcome-wikied'; if (userAuthorized) { //Don't display this link for users who are not new page reviewers or administrators, since the silent function only patrols pages. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (S)','ca-welcome-wikied-s','Activate WelcomeNewStudent script (SKIP welcome message)',null,$('#ca-welcome-wikied').next()); $('#ca-welcome-wikied-s').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled but SKIP adding a welcome template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,1,0,0); } }); } mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (AUTO)','ca-welcome-wikied-a','Activate WelcomeNewStudent script (auto add welcome message)',null,$(prevNode).next()); $('#ca-welcome-wikied-a').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and detect if a welcome template already exists, and adds one if none is detected (EXPERIMENTAL).\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,1,1); } }); mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new instructor','ca-welcome-wikied-i','Activate WelcomeNewStudent script (for instructor)',null,$('#ca-welcome-wikied-a').next()); $('#ca-welcome-wikied-i').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome teacher template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,1); } }); } } } function queryUserAuthorization() { const newPageReviewerCheck = mw.config.get('wgUserGroups').includes('patroller'); const sysopCheck = mw.config.get('wgUserGroups').includes('sysop'); let isAuthorized = newPageReviewerCheck || sysopCheck ? true : false; if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const allowedGlobalGroups = ['steward']; const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { //Check if the user is a member of any of the required global groups. isAuthorized = true; } } else { throw new Error('\'data.query\' does not exist.'); } }).catch(function(error) { console.warn('Error (WelcomeNewStudent): API call to retrieve global group memberships failed. Error: ' + error + '.'); return false; }); } catch (error) { console.warn('Error (WelcomeNewStudent): Parsing API call data for global group memberships failed. Error: ' + error + '.'); return false; } } return isAuthorized; } function getLatestRevisionID(api,title) { let revisionID = null; return api.get({ action: 'query', prop: 'revisions', titles: title, rvprop: 'ids', rvlimit: 1, rvdir: 'newer', format: 'json' }).then(function(data) { let page = Object.values(data.query.pages)[0]; if (page.missing !== undefined) { //The current page doesn't exist if page.missing is set to a value. revisionID = null; } else { revisionID = page.revisions[0].revid; } return revisionID; }); } function patrolPage(api,title) { return getLatestRevisionID(api,title) .then(function(revid) { $('#mw-content-text').append('<p>Marking page <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled...</p>'); if (!revid) {//If null is returned from the function, the current page doesn't exist. Append an error message stating this, then end this function. $('#mw-content-text').append('<p><b>Cannot mark</b> <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Page does not exist.</p>'); return; } return api.postWithToken('patrol', { action: 'patrol', revid: revid, format: 'json' }).then(function() { $('#mw-content-text').append('<p><b>Success</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Error message: ' + error + '. RevID: ' + revid + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not mark page ' + title + ' as patrolled. Error message: ' + error + '. RevID: ' + revid + '.'); }); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> retrieving latest revision from <a href="' + mw.util.getUrl(title) + '">' + title + '</a>. Error message: ' + error + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not retrieve latest revision ID from page ' + title + '. Error message: ' + error + '.'); }); } function addWelcomeMessage(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const talkPage = 'User talk:' + targetUsername; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; api.postWithToken('csrf', { action: 'edit', title: talkPage, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).then(function() { $('#mw-content-text').append( '<p><b>Success</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>: Created new section with ' + templateText + '.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); }).catch(function(error) { $('#mw-content-text').append( '<p><b>Error</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>. Error message: ' + error + '.</p>' + '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); console.warn('Error (WelcomeNewStudent): Could not edit User talk:' + targetUsername + '. Error message: ' + error + '.'); }); } function addWelcomeMessageAuto(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>'; //const talkPage = 'User talk:' + targetUsername; const sectionTitle = 'Welcome'; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUsername, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => section.line.includes(sectionTitle)); if (existingSection) { // A section with a title containing 'Welcome' already exists. Skip adding a welcome message. $('#mw-content-text').append('<p><b>Skipped</b> editing '+ href +' and leaving welcome message (WELCOME MESSAGE DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { // No section exists with a title containing 'Welcome', so create it and add the welcome message. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUsername, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + ': Created new section with ' + templateText + ' (WELCOME MESSAGE NOT DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (WelcomeNewStudent): Adding welcome message to User talk:' + targetUsername + ' FAILED (AUTO MODE). Error details: ' + error + '.'); $('#mw-content-text').append('<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }); } }); } function skipWelcomeMessage() { const targetUsername = mw.config.get('wgRelevantUserName'); $('#mw-content-text').append( '<p><b>Skipped</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a> and leaving welcome message.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); } function runScript(auth,pages,api,isSilent,isAuto,isInstructor) { $('#ca-welcome-wikied').text('Please wait...'); $('#ca-welcome-wikied-s').text('Please wait...'); $('#ca-welcome-wikied-i').text('Please wait...'); if (auth) { Promise.allSettled( pages.map(function(page) { return patrolPage(api,page); }) ).then(function() { if (!isAuto) { if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } else { addWelcomeMessageAuto(isInstructor,api); } }); } else { $('#mw-content-text').append('<p><b>Skipped</b> searching for and marking common WikiEd user pages as patrolled. <b>Missing required user permissions.</b></p>'); if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } } //</nowiki> mtk2s021tfekop833gqk7yg9f981rep 760326 760321 2026-09-04T20:08:37Z Oshwah 26741 Update auto function to use array of strings to search and compare instead of only one var containing a string. 760326 javascript text/javascript //<nowiki> var wns_option_enableExtendedFeatures; $.when(mw.loader.using(['mediawiki.util']), $.ready).done(init); function init() { const myUsername = mw.config.get('wgUserName'); //Script operator's username. const targetUsername = mw.config.get('wgRelevantUserName'); //Target account's username. const pageNamespace = mw.config.get('wgNamespaceNumber'); //The namespace number of the current page. const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Name of the current special page. If we're not on a special page, this returns false. //Only run this script if we're currently viewing another registered account's user space, user talk space, or Special:Contributions page. if([-1,2,3].indexOf(pageNamespace) !== -1 && (!specialPageName || specialPageName == 'Contributions') && targetUsername && myUsername != targetUsername && targetUsername.substring(0, 2) !== '~2' && !mw.util.isIPAddress(targetUsername)) { //If the script operator does not have 'wns_option_enableExtendedFeatures = true;' added to their common.js page, set the extended features flag to false by default. const enableExtendedFeatures = (typeof wns_option_enableExtendedFeatures == 'undefined')?(false):(wns_option_enableExtendedFeatures); const userAuthorized = queryUserAuthorization(); const api = new mw.Api(); const pagesList = [ 'User:' + targetUsername, 'User talk:' + targetUsername, 'User:' + targetUsername + '/sandbox', 'User:' + targetUsername + '/be bold', 'User:' + targetUsername + '/Evaluate an Article', 'User:' + targetUsername + '/Choose an Article', 'User talk:' + targetUsername + '/talk page tutorial' ]; //Insert this link above the first QuickBlockScript menu item ('ca-qbs-sockblock-s') - see User:Oshwah/QuickBlockScript.js. //The function falls back to inserting the link at the end of the portlet if 'ca-qbs-sockblock-s' doesn't exist. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student','ca-welcome-wikied','Activate WelcomeNewStudent script',null,'#ca-qbs-sockblock-s'); $('#ca-welcome-wikied').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome student template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,0); } }); if (enableExtendedFeatures) { const prevNode = userAuthorized ? '#ca-welcome-wikied-s' : '#ca-welcome-wikied'; if (userAuthorized) { //Don't display this link for users who are not new page reviewers or administrators, since the silent function only patrols pages. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (S)','ca-welcome-wikied-s','Activate WelcomeNewStudent script (SKIP welcome message)',null,$('#ca-welcome-wikied').next()); $('#ca-welcome-wikied-s').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled but SKIP adding a welcome template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,1,0,0); } }); } mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (AUTO)','ca-welcome-wikied-a','Activate WelcomeNewStudent script (auto add welcome message)',null,$(prevNode).next()); $('#ca-welcome-wikied-a').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and detect if a welcome template already exists, and adds one if none is detected (EXPERIMENTAL).\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,1,1); } }); mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new instructor','ca-welcome-wikied-i','Activate WelcomeNewStudent script (for instructor)',null,$('#ca-welcome-wikied-a').next()); $('#ca-welcome-wikied-i').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome teacher template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,1); } }); } } } function queryUserAuthorization() { const newPageReviewerCheck = mw.config.get('wgUserGroups').includes('patroller'); const sysopCheck = mw.config.get('wgUserGroups').includes('sysop'); let isAuthorized = newPageReviewerCheck || sysopCheck ? true : false; if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const allowedGlobalGroups = ['steward']; const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { //Check if the user is a member of any of the required global groups. isAuthorized = true; } } else { throw new Error('\'data.query\' does not exist.'); } }).catch(function(error) { console.warn('Error (WelcomeNewStudent): API call to retrieve global group memberships failed. Error: ' + error + '.'); return false; }); } catch (error) { console.warn('Error (WelcomeNewStudent): Parsing API call data for global group memberships failed. Error: ' + error + '.'); return false; } } return isAuthorized; } function getLatestRevisionID(api,title) { let revisionID = null; return api.get({ action: 'query', prop: 'revisions', titles: title, rvprop: 'ids', rvlimit: 1, rvdir: 'newer', format: 'json' }).then(function(data) { let page = Object.values(data.query.pages)[0]; if (page.missing !== undefined) { //The current page doesn't exist if page.missing is set to a value. revisionID = null; } else { revisionID = page.revisions[0].revid; } return revisionID; }); } function patrolPage(api,title) { return getLatestRevisionID(api,title) .then(function(revid) { $('#mw-content-text').append('<p>Marking page <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled...</p>'); if (!revid) {//If null is returned from the function, the current page doesn't exist. Append an error message stating this, then end this function. $('#mw-content-text').append('<p><b>Cannot mark</b> <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Page does not exist.</p>'); return; } return api.postWithToken('patrol', { action: 'patrol', revid: revid, format: 'json' }).then(function() { $('#mw-content-text').append('<p><b>Success</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Error message: ' + error + '. RevID: ' + revid + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not mark page ' + title + ' as patrolled. Error message: ' + error + '. RevID: ' + revid + '.'); }); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> retrieving latest revision from <a href="' + mw.util.getUrl(title) + '">' + title + '</a>. Error message: ' + error + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not retrieve latest revision ID from page ' + title + '. Error message: ' + error + '.'); }); } function addWelcomeMessage(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const talkPage = 'User talk:' + targetUsername; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; api.postWithToken('csrf', { action: 'edit', title: talkPage, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).then(function() { $('#mw-content-text').append( '<p><b>Success</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>: Created new section with ' + templateText + '.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); }).catch(function(error) { $('#mw-content-text').append( '<p><b>Error</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>. Error message: ' + error + '.</p>' + '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); console.warn('Error (WelcomeNewStudent): Could not edit User talk:' + targetUsername + '. Error message: ' + error + '.'); }); } function addWelcomeMessageAuto(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>'; //const talkPage = 'User talk:' + targetUsername; const sectionTitle = ['Welcome','welcome']; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUsername, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => sectionTitle.some(title => section.line.includes(title))); if (existingSection) { // A section with a title containing 'Welcome' already exists. Skip adding a welcome message. $('#mw-content-text').append('<p><b>Skipped</b> editing '+ href +' and leaving welcome message (WELCOME MESSAGE DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { // No section exists with a title containing 'Welcome', so create it and add the welcome message. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUsername, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + ': Created new section with ' + templateText + ' (WELCOME MESSAGE NOT DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (WelcomeNewStudent): Adding welcome message to User talk:' + targetUsername + ' FAILED (AUTO MODE). Error details: ' + error + '.'); $('#mw-content-text').append('<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }); } }); } function skipWelcomeMessage() { const targetUsername = mw.config.get('wgRelevantUserName'); $('#mw-content-text').append( '<p><b>Skipped</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a> and leaving welcome message.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); } function runScript(auth,pages,api,isSilent,isAuto,isInstructor) { $('#ca-welcome-wikied').text('Please wait...'); $('#ca-welcome-wikied-s').text('Please wait...'); $('#ca-welcome-wikied-i').text('Please wait...'); if (auth) { Promise.allSettled( pages.map(function(page) { return patrolPage(api,page); }) ).then(function() { if (!isAuto) { if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } else { addWelcomeMessageAuto(isInstructor,api); } }); } else { $('#mw-content-text').append('<p><b>Skipped</b> searching for and marking common WikiEd user pages as patrolled. <b>Missing required user permissions.</b></p>'); if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } } //</nowiki> 4srvtidpcxrqnfxj4pp8ql3ym7ixtl8 760330 760326 2026-09-04T20:14:33Z Oshwah 26741 Set auto function to leave student welcome template. 760330 javascript text/javascript //<nowiki> var wns_option_enableExtendedFeatures; $.when(mw.loader.using(['mediawiki.util']), $.ready).done(init); function init() { const myUsername = mw.config.get('wgUserName'); //Script operator's username. const targetUsername = mw.config.get('wgRelevantUserName'); //Target account's username. const pageNamespace = mw.config.get('wgNamespaceNumber'); //The namespace number of the current page. const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Name of the current special page. If we're not on a special page, this returns false. //Only run this script if we're currently viewing another registered account's user space, user talk space, or Special:Contributions page. if([-1,2,3].indexOf(pageNamespace) !== -1 && (!specialPageName || specialPageName == 'Contributions') && targetUsername && myUsername != targetUsername && targetUsername.substring(0, 2) !== '~2' && !mw.util.isIPAddress(targetUsername)) { //If the script operator does not have 'wns_option_enableExtendedFeatures = true;' added to their common.js page, set the extended features flag to false by default. const enableExtendedFeatures = (typeof wns_option_enableExtendedFeatures == 'undefined')?(false):(wns_option_enableExtendedFeatures); const userAuthorized = queryUserAuthorization(); const api = new mw.Api(); const pagesList = [ 'User:' + targetUsername, 'User talk:' + targetUsername, 'User:' + targetUsername + '/sandbox', 'User:' + targetUsername + '/be bold', 'User:' + targetUsername + '/Evaluate an Article', 'User:' + targetUsername + '/Choose an Article', 'User talk:' + targetUsername + '/talk page tutorial' ]; //Insert this link above the first QuickBlockScript menu item ('ca-qbs-sockblock-s') - see User:Oshwah/QuickBlockScript.js. //The function falls back to inserting the link at the end of the portlet if 'ca-qbs-sockblock-s' doesn't exist. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student','ca-welcome-wikied','Activate WelcomeNewStudent script',null,'#ca-qbs-sockblock-s'); $('#ca-welcome-wikied').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome student template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,0); } }); if (enableExtendedFeatures) { const prevNode = userAuthorized ? '#ca-welcome-wikied-s' : '#ca-welcome-wikied'; if (userAuthorized) { //Don't display this link for users who are not new page reviewers or administrators, since the silent function only patrols pages. mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (S)','ca-welcome-wikied-s','Activate WelcomeNewStudent script (SKIP welcome message)',null,$('#ca-welcome-wikied').next()); $('#ca-welcome-wikied-s').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled but SKIP adding a welcome template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,1,0,0); } }); } mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new student (AUTO)','ca-welcome-wikied-a','Activate WelcomeNewStudent script (auto add welcome message)',null,$(prevNode).next()); $('#ca-welcome-wikied-a').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and detect if a welcome template already exists, and adds one if none is detected (EXPERIMENTAL).\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,1,0); } }); mw.util.addPortletLink('p-cactions','javascript:void(0)','Welcome new instructor','ca-welcome-wikied-i','Activate WelcomeNewStudent script (for instructor)',null,$('#ca-welcome-wikied-a').next()); $('#ca-welcome-wikied-i').on('click', function() { if (confirm('This will mark ' + targetUsername + '\'s WikiEd pages as patrolled and add the welcome teacher template to their user talk page.\n\nARE YOU SURE YOU WANT TO PROCEED?')) { runScript(userAuthorized,pagesList,api,0,0,1); } }); } } } function queryUserAuthorization() { const newPageReviewerCheck = mw.config.get('wgUserGroups').includes('patroller'); const sysopCheck = mw.config.get('wgUserGroups').includes('sysop'); let isAuthorized = newPageReviewerCheck || sysopCheck ? true : false; if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const allowedGlobalGroups = ['steward']; const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { //Check if the user is a member of any of the required global groups. isAuthorized = true; } } else { throw new Error('\'data.query\' does not exist.'); } }).catch(function(error) { console.warn('Error (WelcomeNewStudent): API call to retrieve global group memberships failed. Error: ' + error + '.'); return false; }); } catch (error) { console.warn('Error (WelcomeNewStudent): Parsing API call data for global group memberships failed. Error: ' + error + '.'); return false; } } return isAuthorized; } function getLatestRevisionID(api,title) { let revisionID = null; return api.get({ action: 'query', prop: 'revisions', titles: title, rvprop: 'ids', rvlimit: 1, rvdir: 'newer', format: 'json' }).then(function(data) { let page = Object.values(data.query.pages)[0]; if (page.missing !== undefined) { //The current page doesn't exist if page.missing is set to a value. revisionID = null; } else { revisionID = page.revisions[0].revid; } return revisionID; }); } function patrolPage(api,title) { return getLatestRevisionID(api,title) .then(function(revid) { $('#mw-content-text').append('<p>Marking page <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled...</p>'); if (!revid) {//If null is returned from the function, the current page doesn't exist. Append an error message stating this, then end this function. $('#mw-content-text').append('<p><b>Cannot mark</b> <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Page does not exist.</p>'); return; } return api.postWithToken('patrol', { action: 'patrol', revid: revid, format: 'json' }).then(function() { $('#mw-content-text').append('<p><b>Success</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> marking <a href="' + mw.util.getUrl(title) + '">' + title + '</a> as patrolled. Error message: ' + error + '. RevID: ' + revid + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not mark page ' + title + ' as patrolled. Error message: ' + error + '. RevID: ' + revid + '.'); }); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> retrieving latest revision from <a href="' + mw.util.getUrl(title) + '">' + title + '</a>. Error message: ' + error + '.</p>'); console.warn('Error (WelcomeNewStudent): Could not retrieve latest revision ID from page ' + title + '. Error message: ' + error + '.'); }); } function addWelcomeMessage(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const talkPage = 'User talk:' + targetUsername; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; api.postWithToken('csrf', { action: 'edit', title: talkPage, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).then(function() { $('#mw-content-text').append( '<p><b>Success</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>: Created new section with ' + templateText + '.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); }).catch(function(error) { $('#mw-content-text').append( '<p><b>Error</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>. Error message: ' + error + '.</p>' + '<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); console.warn('Error (WelcomeNewStudent): Could not edit User talk:' + targetUsername + '. Error message: ' + error + '.'); }); } function addWelcomeMessageAuto(isInstructor,api) { const targetUsername = mw.config.get('wgRelevantUserName'); const href = '<a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a>'; //const talkPage = 'User talk:' + targetUsername; const sectionTitle = ['Welcome','welcome']; const templateText = isInstructor ? '{{subst:Welcome teacher}} ~~~~' : '{{subst:Welcome student}} ~~~~'; let existingSection = null; api.get({ action: 'parse', page: 'User talk:' + targetUsername, prop: 'tocdata', formatversion: 2 }).always(function(data) { const sections = data.parse?.tocdata?.sections || []; existingSection = sections.find(section => sectionTitle.some(title => section.line.includes(title))); if (existingSection) { // A section with a title containing 'Welcome' already exists. Skip adding a welcome message. $('#mw-content-text').append('<p><b>Skipped</b> editing '+ href +' and leaving welcome message (WELCOME MESSAGE DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { // No section exists with a title containing 'Welcome', so create it and add the welcome message. api.postWithToken('csrf', { action: 'edit', title: 'User talk:' + targetUsername, watchlist: 'unwatch', section: 'new', sectiontitle: 'Welcome to Wikipedia!', text: templateText, summary: 'Added welcome message for new ' + (isInstructor ? 'instructor' : 'student') + ' (using [[User:Oshwah/WelcomeNewStudent|WNS]])' }).done(function() { $('#mw-content-text').append('<p><b>Success</b> editing ' + href + ': Created new section with ' + templateText + ' (WELCOME MESSAGE NOT DETECTED AS PRESENT).</p>'); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b> editing ' + href + '. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (WelcomeNewStudent): Adding welcome message to User talk:' + targetUsername + ' FAILED (AUTO MODE). Error details: ' + error + '.'); $('#mw-content-text').append('<p><b>Completed with errors (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); }); } }); } function skipWelcomeMessage() { const targetUsername = mw.config.get('wgRelevantUserName'); $('#mw-content-text').append( '<p><b>Skipped</b> editing <a href="' + mw.util.getUrl('User talk:' + targetUsername) + '">User talk:' + targetUsername + '</a> and leaving welcome message.</p>' + '<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>' ); } function runScript(auth,pages,api,isSilent,isAuto,isInstructor) { $('#ca-welcome-wikied').text('Please wait...'); $('#ca-welcome-wikied-s').text('Please wait...'); $('#ca-welcome-wikied-i').text('Please wait...'); if (auth) { Promise.allSettled( pages.map(function(page) { return patrolPage(api,page); }) ).then(function() { if (!isAuto) { if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } else { addWelcomeMessageAuto(isInstructor,api); } }); } else { $('#mw-content-text').append('<p><b>Skipped</b> searching for and marking common WikiEd user pages as patrolled. <b>Missing required user permissions.</b></p>'); if (!isSilent) { addWelcomeMessage(isInstructor,api); } else { skipWelcomeMessage(); } } } //</nowiki> mohn286otcqyb59j2vpgf3a3d1ezqeo User:Enbi/fortress.yaml 2 177499 760332 759376 2026-09-04T21:34:00Z Enbi 72574 760332 wikitext text/x-wiki Fortress config file. <syntaxhighlight lang="yaml"> global: report: srg: lock: regex: '\{\{\s*([Ll]uxotool|[Ll]ock[Hh]ide|[Ll][Hh]|[Mm]ulti[Ll]ock)[^}]*?\|\s*(\d+\s*=\s*)?$USER\s*(\||\}\})' summary: "Requesting global lock for [[Special:CentralAuth/$1|$1]] (Fortress-Beta)" structure: "=== Global lock for $1 ===\n{{Status}}\n*{{LockHide|$1}}\n$2 ~~~~" sectionName: "Requests for global (un)lock and (un)hiding" domain: "test.wikipedia.org" gsr: # temporary fill in regex: '\{\{\s*([Ll]uxotool|[Ll]ock[Hh]ide|[Ll][Hh]|[Mm]ulti[Ll]ock)[^}]*?\|\s*(\d+\s*=\s*)?$USER\s*(\||\}\})' summary: "Requesting global lock for [[Special:CentralAuth/$1|$1]] (Fortress-Beta)" structure: "=== Global lock for $1 ===\n{{Status}}\n*{{LockHide|$1}}\n$2 ~~~~" sectionName: "Requests for global (un)lock and (un)hiding" domain: "test.wikipedia.org" enwiki: warn: summary: "Warning [[Special:Contributions/$1|$1]]: {{[[Template:$2|$2]]}}" months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ] warnings: - name: Vandalism template: vandalism type: "tiered" levels: 5 summary: non-constructive edits category: vandalism tag: vandalism - name: Subtle vandalism template: subtle summary: potentaially non-constructive edits type: "tiered" levels: 4 category: vandalism - name: Image vandalism template: image type: tiered levels: 4 summary: inappropriate image additions details: Inappropriate images added to articles category: vandalism - name: Joke edits template: joke summary: non-constructive edits category: vandalism type: tiered levels: 4 - name: Personal attacks template: npa summary: non-constructive edits levels: 4 type: tiered category: conduct - name: Unsourced template: unsourced summary: unsourced additions levels: 4 tag: unsourced type: tiered category: conduct - name: Unexplained removal template: delete summary: unexplained content removal type: tiered levels: 4 category: conduct - name: MOS template: mos type: tiered levels: 4 category: conduct summary: potentially disruptive details: Manual of style violations - name: Disruptive editing template: disruptive type: tiered levels: 5 tag: disruptive summary: potentially disruptive edits category: disruptive - name: Factual errors type: tiered template: error summary: factual errors category: disruptive levels: 4 - name: Test edits type: tiered levels: 3 template: test summary: Editing tests category: disruptive tag: test - name: Non-neutral template: npov summary: non-neutral-edits type: tiered levels: 4 category: npov - name: Promotional template: promo summary: promotional content added category: npov type: tiered levels: 4 - name: English variant template: engvar type: single summary: incorrect English variants category: single report: aiv: true regex: "..." summary: "Reporting $1 (Fortress-Beta)" structure: "*{{vandal|$1}} &ndash; $2" sectionIndex: 99 gs: false rollback: baseSummary: "Reverting $1 by [[Special:Contributions/$2|$2]] (Fortress-Beta)" noWarnSummary: "Reverting edits (Fortress-Beta)" testwiki: warn: summary: "Warning [[Special:Contributions/$1|$1]]: {{[[Template:$2|$2]]}}" months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ] warnings: - name: Vandalism template: vandalism type: "tiered" levels: 5 summary: non-constructive edits category: vandalism tag: vandalism - name: Subtle vandalism template: subtle summary: potentaially non-constructive edits type: "tiered" levels: 4 category: vandalism - name: Image vandalism template: image type: tiered levels: 4 summary: inappropriate image additions details: Inappropriate images added to articles category: vandalism - name: Joke edits template: joke summary: non-constructive edits category: vandalism type: tiered levels: 4 - name: Personal attacks template: npa summary: non-constructive edits levels: 4 type: tiered category: conduct - name: Unsourced template: unsourced summary: unsourced additions levels: 4 tag: unsourced type: tiered category: conduct - name: Unexplained removal template: delete summary: unexplained content removal type: tiered levels: 4 category: conduct - name: MOS template: mos type: tiered levels: 4 category: conduct summary: potentially disruptive details: Manual of style violations - name: Disruptive editing template: disruptive type: tiered levels: 5 tag: disruptive summary: potentially disruptive edits category: disruptive - name: Factual errors type: tiered template: error summary: factual errors category: disruptive levels: 4 - name: Test edits type: tiered levels: 3 template: test summary: Editing tests category: disruptive tag: test - name: Non-neutral template: npov summary: non-neutral-edits type: tiered levels: 4 category: npov - name: Promotional template: promo summary: promotional content added category: npov type: tiered levels: 4 - name: English variant template: engvar type: single summary: incorrect English variants category: single report: aiv: true regex: "..." summary: "Reporting $1 (Fortress-Beta)" structure: "*{{vandal|$1}} &ndash; $2" sectionIndex: 99 gs: false rollback: baseSummary: "Reverting $1 by [[Special:Contributions/$2|$2]] (Fortress-Beta)" noWarnSummary: "Reverting edits (Fortress-Beta)" </syntaxhighlight> 7wssjbqn61k8hrbimeq51b8whi4zeoc Test000 0 177516 760338 758862 2026-09-04T22:03:09Z Enbi 72574 760338 wikitext text/x-wiki hi 2 avpnf0807gldtjngavg492seuk0wgg3 User:Dreamyshade/common.js 2 177604 760388 759907 2026-09-05T03:42:01Z Dreamyshade 20227 760388 javascript text/javascript <!-- mw.loader.load('http://localhost:4444?ctype=text/javascript&title=afch-dev.js', 'text/javascript'); --> mw.loader.load('https://test.wikipedia.org/wiki/User:Max/NPPChecklistProto.js'); dxbsvas418qbfyy6ecshfwrxe0lw4wz 760389 760388 2026-09-05T03:44:47Z Dreamyshade 20227 760389 javascript text/javascript <!-- mw.loader.load('http://localhost:4444?ctype=text/javascript&title=afch-dev.js', 'text/javascript'); --> importScript('User:Max/NPPChecklistProto.js'); su54ie4pkytzbibjc0nsc2a0lqmrk3t Talk:Testing AFCH orphan 1 177612 760390 759194 2026-09-05T03:45:45Z Dreamyshade 20227 Clear reviews 760390 wikitext text/x-wiki {{WikiProject Biography|living=yes}} 0f0fcax2uxy4l9qwal9b70n3qs2f75p 760391 760390 2026-09-05T03:58:07Z Dreamyshade 20227 NPP checklist: recording page review 760391 wikitext text/x-wiki {{WikiProject Biography|living=yes}} == NPP review checklists == === Dreamyshade === {{NPP checklist |checked_for_copyvio = y |checked_CSD_criteria = unknown |checked_whether_to_draftify = y |checked_notability = |checked_title = |comments = |sign = [[User:Dreamyshade|Dreamyshade]] ([[User talk:Dreamyshade|talk]]) 03:58, 5 September 2026 (UTC) }} 2a5g9t6bezp6f8p3k26hu5l1qxn4dok User:Oshwah/Contribs-HideNoUsername.js 2 177642 760358 755596 2026-09-04T23:54:15Z Oshwah 26741 Code optimization. 760358 javascript text/javascript //<nowiki> mw.loader.using('mediawiki.util', function() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Get name of the current special page, e.g. 'AbuseLog'. If we're not on a special page, this returns null. if (specialPageName === 'Contributions') { //Only proceed if this is a special page and if the current special page is Special:Contributions. const targetText = '(no username available)'; //This is the text that we want to remove. mw.hook('wikipage.content').add(function (content) { content.find('.mw-contributions-list li').each(function() { let found = false; let node = null; let walker = document.createTreeWalker(this,NodeFilter.SHOW_TEXT); while ((node = walker.nextNode())) { if (node.nodeValue.includes(targetText)) { node.nodeValue = node.nodeValue.replaceAll(targetText, ''); found = true; } } if (found) { $(this).find('span.mw-usertoollinks.mw-changeslist-links').remove(); } }); }); } }); //</nowiki> 2bcpj30fgp46kcnt4giv8hi8jpj6f83 760359 760358 2026-09-04T23:54:39Z Oshwah 26741 Oshwah moved page [[User:Oshwah/Contribs-RemoveUsernameUnavailable.js]] to [[User:Oshwah/Contribs-HideNoUsername.js]] without leaving a redirect: Rename. 760358 javascript text/javascript //<nowiki> mw.loader.using('mediawiki.util', function() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Get name of the current special page, e.g. 'AbuseLog'. If we're not on a special page, this returns null. if (specialPageName === 'Contributions') { //Only proceed if this is a special page and if the current special page is Special:Contributions. const targetText = '(no username available)'; //This is the text that we want to remove. mw.hook('wikipage.content').add(function (content) { content.find('.mw-contributions-list li').each(function() { let found = false; let node = null; let walker = document.createTreeWalker(this,NodeFilter.SHOW_TEXT); while ((node = walker.nextNode())) { if (node.nodeValue.includes(targetText)) { node.nodeValue = node.nodeValue.replaceAll(targetText, ''); found = true; } } if (found) { $(this).find('span.mw-usertoollinks.mw-changeslist-links').remove(); } }); }); } }); //</nowiki> 2bcpj30fgp46kcnt4giv8hi8jpj6f83 760377 760359 2026-09-05T02:27:37Z Oshwah 26741 Add 'DeletedContributions' as a special page this script will work on. 760377 javascript text/javascript //<nowiki> mw.loader.using('mediawiki.util', function() { const specialPageName = mw.config.get('wgCanonicalSpecialPageName'); //Get name of the current special page, e.g. 'AbuseLog'. If we're not on a special page, this returns null. if (specialPageName === 'Contributions' || specialPageName === 'DeletedContributions') { //Only proceed if this is a special page and if the current special page is Special:Contributions. const targetText = '(no username available)'; //This is the text that we want to remove. mw.hook('wikipage.content').add(function (content) { content.find('.mw-contributions-list li').each(function() { let found = false; let node = null; let walker = document.createTreeWalker(this,NodeFilter.SHOW_TEXT); while ((node = walker.nextNode())) { if (node.nodeValue.includes(targetText)) { node.nodeValue = node.nodeValue.replaceAll(targetText, ''); found = true; } } if (found) { $(this).find('span.mw-usertoollinks.mw-changeslist-links').remove(); } }); }); } }); //</nowiki> a00is0yh56cdvrbicve97ac4zjx6u8l User:Oshwah/RevDelUsername.js 2 177643 760385 755672 2026-09-05T02:54:53Z Oshwah 26741 Copy code from sandbox. 760385 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(init_rdu).catch(function(error) { console.error('Error (RevDel Username): Initialization failed.',error); }); async function init_rdu() { const isUserAuthorized = await queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); await countAndConfirm(reason); } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); const data = await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }); //Parse the list of the user's global group memberships from the returned data. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. isAuthorized = globalUserGroups.some(function(group) { return allowedGlobalGroups.includes(group); }); } else { throw new Error('"data.query" does not exist.'); } } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (RevDel Username): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } function countMapValues(map) { let count = 0; map.forEach(function(values) { count += values.length; }); return count; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionsByTitle = await getVisibleLiveRevisionsByTitle(); let liveRevisionCount = countMapValues(liveRevisionsByTitle); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = await getArchivedRevisionsByTitle(); let archivedCount = countMapValues(archivedByTitle); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds = logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); let total = liveRevisionCount + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionCount + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. userConfirmed = true; await performRevDelOperation(liveRevisionsByTitle,newUsersLogIds,otherLogIds,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } return userConfirmed; } async function performRevDelOperation(liveRevisionsByTitle,newUsersLogIds,otherLogIds,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); const liveRevisionCount = countMapValues(liveRevisionsByTitle); const archivedCount = countMapValues(archivedByTitle); //Process normal live revisions. Revisions must be grouped by page title because RevisionDelete operates on one page at a time. if (liveRevisionCount) { await hideLiveRevisions(liveRevisionsByTitle,reason); } //Process revisions belonging to deleted pages. Archive RevisionDelete identifies revisions by timestamp, not revision ID. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entries. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getVisibleLiveRevisionsByTitle(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = await getArchivedRevisionsByTitle(); let remainingLiveCount = countMapValues(remainingLive); let remainingArchivedCount = countMapValues(remainingArchived); let remainingTotal = remainingLiveCount + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionCount + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the target user, group them by page title, and verify that the username is still publicly visible. async function getVisibleLiveRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); let titleByRevisionId = new Map(); rows.forEach(function(row) { if (row.revid) { titleByRevisionId.set(row.revid,row.title); } }); let revisionIds = Array.from(titleByRevisionId.keys()); let visibleRevisionIds = await getVisibleLiveRevisionIds(revisionIds); let byTitle = new Map(); visibleRevisionIds.forEach(function(revid) { let title = titleByRevisionId.get(revid); if (title) { let ids = byTitle.get(title) || []; ids.push(revid); byTitle.set(title,unique(ids)); } }); return byTitle; } //Check live revision IDs with prop=revisions because usercontribs does not expose the userhidden flag. async function getVisibleLiveRevisionIds(revisionIds) { const BATCH_SIZE = 50; let api = new mw.Api(); let visibleRevisionIds = []; let batches = chunks(revisionIds,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { let data = await api.get({ action: 'query', prop: 'revisions', revids: batches[i].join('|'), rvprop: 'ids|user', format: 'json', formatversion: 2, maxlag: 5 }); let pages = (data.query && data.query.pages) || []; pages.forEach(function(page) { (page.revisions || []).forEach(function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { visibleRevisionIds.push(revision.revid); } }); }); } return unique(visibleRevisionIds); } //Find revisions by the target user belonging to pages which have been deleted. RevisionDelete type=archive requires a target page title and uses the archived revision timestamp as the ID. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|timestamp|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let timestamps = byTitle.get(page.title) || []; (page.revisions || []).forEach(function(revision) { if (revision.timestamp && !hasOwn(revision,'userhidden')) { timestamps.push(revision.timestamp); } }); if (timestamps.length) { byTitle.set(page.title,unique(timestamps)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row,'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform a RevisionDelete request. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Live revisions must be processed separately for each page title. async function hideLiveRevisions(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Live edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('revision',batches[j],reason,title,'user'); } } } //Archived revisions must be processed separately for each deleted page title. For type=archive, the IDs are archive timestamps. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let timestamps = entries[i][1]; let batches = chunks(timestamps,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title,'user'); } } } //</nowiki> 6y4h01a9gbesg3q34vp7pj0mtbwe5dn User:Cryptocurrency777/sandbox13 2 178198 760283 2026-09-04T13:32:29Z Cryptocurrency777 73698 Created page with "{{Infobox musical artist | name = Eris | alias = erisy1755, erisislife, iloveyouerisy | origin = USA | genre = Hyperpop | instrument = Vocals | years_active = 2024-Present }} '''Eris''', also known as erisy1755, erisislife, iloveyouerisy, is a digtal visual artist, composer, singer. == Career == They started in late 2024 under a youtube account named JeetsonEris, where they created animated visualizers for h..." 760283 wikitext text/x-wiki {{Infobox musical artist | name = Eris | alias = erisy1755, erisislife, iloveyouerisy | origin = USA | genre = Hyperpop | instrument = Vocals | years_active = 2024-Present }} '''Eris''', also known as erisy1755, erisislife, iloveyouerisy, is a digtal visual artist, composer, singer. == Career == They started in late 2024 under a youtube account named JeetsonEris, where they created animated visualizers for hyperpop artists, such as AfterDRK, CR0T0N, and [[Anti-Light]], with their last visualizer created in January 2026 for [[UpVampin]]'s song "[[Living Hell]]". Since 2025, they started creating their own music. == Personal life == They are queer. otozt4nbca96l1qx7rbkmzzxg4dpjub User:RAPTOR7762/sandbox 2 178199 760284 2026-09-04T14:54:50Z RAPTOR7762 75074 Created page with "<div style="width:250px; height:250px; background: url("https://upload.wikimedia.org/wikipedia/commons/1/1a/some_image.svg");"></div> https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original" 760284 wikitext text/x-wiki <div style="width:250px; height:250px; background: url("https://upload.wikimedia.org/wikipedia/commons/1/1a/some_image.svg");"></div> https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original 5o6prpbl3tkd1nbujzf7p2ikltejtqp User:Alhassan Mohammed Awal/Starter kit/Content categories 2 178200 760288 2026-09-04T17:37:38Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760288 wikitext text/x-wiki <templatestyles src="Starter kit/Content categories/styles.css" /> <div class="skit-catnav"> <div class="skit-catnav__item">Arts & Literature</div> <div class="skit-catnav__item">Countries & Geography</div> <div class="skit-catnav__item">Science & Technology</div> <div class="skit-catnav__item">History & Events</div> <div class="skit-catnav__item">Requested Articles</div> </div> <noinclude>[[Category:Starter kit templates]]</noinclude> g7jh6h6wsq5nq2hbq0nb4xqr0ebtqxe User:Alhassan Mohammed Awal/Starter kit/Welcome banner 2 178201 760289 2026-09-04T17:37:51Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760289 wikitext text/x-wiki <div style="text-align: center; font-family: 'Linux Libertine', Georgia, Times, serif; margin: 1.5em 0;"> <span style="font-size: 2.3em; line-height: 1.2;">Welcome to {{#language:{{PAGELANGUAGE}}}} Wikipedia</span><br/> <span style="font-size: 1.1em; color: #54595d;">The free encyclopedia that anyone can edit</span><br/> <span style="font-size: 0.95em; color: #72777d; margin-top: 0.5em; display: inline-block;">{{NUMBEROFACTIVEUSERS}} active editors • '''{{NUMBEROFARTICLES}}''' articles in this {{SITENAME}}</span> </div> <noinclude>[[Category:Starter kit templates]]</noinclude> 2gaz0l1265uxned195lmw3a6r0oocxa User:Alhassan Mohammed Awal/Starter kit/Featured article 2 178202 760290 2026-09-04T17:38:17Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760290 wikitext text/x-wiki <div style="display: flow-root; border:1px solid #cef2e0;border-radius:4px;padding:8px;background:#f5fffa;margin-bottom:2px;"> <div style="padding:4px 12px;margin-bottom:8px;border:1px solid #a3bfb1;border-radius:4px;background:#cef2e0;">'''Featured article'''</div> <!-- Add a short excerpt from any well-written article on your wiki. No need to update daily — refresh when a new article is ready. To add an image: [[File:Filename.jpg|112px|left|alt=description]] --> Add article title here – Add a short excerpt here (2–3 sentences). Introduce the topic clearly so readers want to learn more. <!-- Replace "Full article..." below with: [[Article name|Full article...]] --> <small>(Full article...)</small> </div> <noinclude>[[Category:Starter kit templates]]</noinclude> 87p1q0jeb8rfvmpsag69upwn6r6ws51 User:Alhassan Mohammed Awal/Starter kit/In the news 2 178203 760291 2026-09-04T17:38:24Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760291 wikitext text/x-wiki <div style="display: flow-root; border:1px solid #cedff2;border-radius:4px;padding:8px;background:#f5faff;margin-bottom:2px;"> <div style="padding:4px 12px;margin-bottom:8px;border:1px solid #a3b0bf;border-radius:4px;background:#cedff2;">'''In the news'''</div> <!-- Add recent events or news relevant to your community or topic area. No need to update daily — refresh when something noteworthy happens. To add an image: [[File:Filename.jpg|64px|right|alt=description]] --> * Add a recent event or news item and link to a relevant article here. * Add a recent event or news item and link to a relevant article here. * Add a recent event or news item and link to a relevant article here. </div> <noinclude>[[Category:Starter kit templates]]</noinclude> cbe53ipg70fxuemrvvszrqqx7sth49v User:Alhassan Mohammed Awal/Starter kit/Did you know 2 178204 760292 2026-09-04T17:38:30Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760292 wikitext text/x-wiki <div style="display: flow-root; border:1px solid #cef2e0;border-radius:4px;padding:8px;background:#f5fffa;margin-bottom:2px;"> <div style="padding:4px 12px;margin-bottom:8px;border:1px solid #a3bfb1;border-radius:4px;background:#cef2e0;">'''Did you know ...'''</div> <!-- Update with 3–5 interesting facts from recently created or expanded articles. No need to update daily — refresh when new articles are added. To add an image: [[File:Filename.jpg|80px|left|alt=description]] --> * ... that add an interesting fact and link to a relevant article here? * ... that add an interesting fact and link to a relevant article here? * ... that add an interesting fact and link to a relevant article here? </div> <noinclude>[[Category:Starter kit templates]]</noinclude> 9edp7vp4p83eig6ls3w6d61u8zw51xx User:Alhassan Mohammed Awal/Starter kit/On this day 2 178205 760293 2026-09-04T17:38:36Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760293 wikitext text/x-wiki <div style="display: flow-root; border:1px solid #cedff2;border-radius:4px;padding:8px;background:#f5faff;margin-bottom:2px;"> <div style="padding:4px 12px;margin-bottom:8px;border:1px solid #a3b0bf;border-radius:4px;background:#cedff2;">'''On this day'''</div> <!-- Add 2–3 historical events relevant to your community or topic area. No need to update daily — refresh occasionally as your wiki grows. To add an image: [[File:Filename.jpg|80px|right|alt=description]] --> Add a date here * Add a historical event and link to a relevant article here. * Add a historical event and link to a relevant article here. </div> <noinclude>[[Category:Starter kit templates]]</noinclude> 4p7vktxyt5mhesvw4qyfpmxvvi2qijs User:Alhassan Mohammed Awal/Starter kit/Featured picture 2 178206 760294 2026-09-04T17:38:43Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760294 wikitext text/x-wiki <div style="display: flow-root; border:1px solid #ddcef2;border-radius:4px;padding:8px;background:#faf5ff;margin-top:4px;"> <div style="padding:4px 12px;margin-bottom:8px;border:1px solid #afa3bf;border-radius:4px;background:#ddcef2;">'''Featured picture'''</div> <!-- Add any image that represents your wiki well — from Commons or locally uploaded. No need to update daily — refresh whenever a good image is available. To add the image: [[File:Filename.jpg|320px|left|alt=description]] --> Add image title here Add a description of the image here (2–3 sentences). <small>''Credit: Add photographer or source credit here''</small> </div> <noinclude>[[Category:Starter kit templates]]</noinclude> oyggvmjszbcayipcna8ezh73x5cwbdz User:Alhassan Mohammed Awal/Starter kit/Community resources 2 178207 760295 2026-09-04T17:38:47Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760295 wikitext text/x-wiki <div style="border:1px solid #CBD5E1;border-radius:4px;background:white;overflow:hidden;margin-bottom:2px;"> <div style="background:#F1F5F9;border-bottom:1px solid #CBD5E1;padding:8px 16px;">'''Community resources'''</div> <div style="padding:16px;"> <!-- Add links to pages that help your community connect and collaborate. Replace each placeholder below with a real page, or remove lines that don't apply yet. --> * Add a link to a discussion venue where the community can talk about the wiki. * Add a link to a welcome or help page for new editors. * Add a link to an upcoming or recurring community event. * Add a link to any other community resource relevant to your wiki. </div> </div> <noinclude>[[Category:Starter kit templates]]</noinclude> 9qpbhbxyfrphef8hiaacfno4y7tohtc User:Alhassan Mohammed Awal/Starter kit/Translation resources 2 178208 760296 2026-09-04T17:38:53Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760296 wikitext text/x-wiki <div style="border:1px solid #CBD5E1;border-radius:4px;background:white;overflow:hidden;margin-bottom:2px;"> <div style="background:#F1F5F9;border-bottom:1px solid #CBD5E1;padding:8px 16px;">'''Translation resources'''</div> <div style="padding:16px;"> <!-- Edit: replace placeholder items with real pages once they exist on your wiki. Add or remove items as your community grows. --> * Translate the most-used interface messages for your wiki on [https://translatewiki.net/w/i.php?title=Special:Translate&group=core-mostused&language=<!--LANGUAGE_CODE--> Translatewiki.net] - The essential messages that power your wiki's interface. * [https://en.wikipedia.org/wiki/Wikipedia:Five_pillars Wikipedia's Five Pillars] - The core principles of Wikipedia. Translate these into your language as a starting point for your wiki's policies. * [https://meta.wikimedia.org/wiki/List_of_articles_every_Wikipedia_should_have 1,000 articles every Wikipedia should have] - A list of essential topics and a great guide for deciding what to write first. * Add a link to a translation guide or resource relevant to your community. </div> </div> <noinclude>[[Category:Starter kit templates]]</noinclude> 9otl2wtitymgbojg52quzf5se6sxb60 User:Alhassan Mohammed Awal/Starter kit/Wikipedia's sister projects 2 178209 760297 2026-09-04T17:38:58Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760297 wikitext text/x-wiki <div style="border:1px solid #CBD5E1;border-radius:4px;background:#ffffff;overflow:hidden;margin-bottom:16px;"> <div style="background:#F8FAFC;border-bottom:1px solid #CBD5E1;padding:8px 16px;font-weight:bold;">Wikipedia's sister projects</div> <div style="padding:16px;"> Wikipedia is written by volunteer editors and hosted by the [https://wikimediafoundation.org/ Wikimedia Foundation], a non-profit organization that also hosts a range of other volunteer [https://wikimediafoundation.org/our-work/wikimedia-projects/ projects]: <ul style="list-style:none;margin:8px 0 0 0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:8px;"> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Commons-logo.svg|31px|link=https://commons.wikimedia.org/|alt=Commons logo]]<span>[https://commons.wikimedia.org/ Commons]<br/><small style="color:#555;">Free media repository</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:MediaWiki-2020-icon.svg|35px|link=https://www.mediawiki.org/|alt=MediaWiki logo]]<span>[https://www.mediawiki.org/ MediaWiki]<br/><small style="color:#555;">Wiki software development</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikimedia Community Logo.svg|35px|link=https://meta.wikimedia.org/|alt=Meta-Wiki logo]]<span>[https://meta.wikimedia.org/ Meta-Wiki]<br/><small style="color:#555;">Wikimedia project coordination</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikibooks-logo.svg|35px|link=https://www.wikibooks.org/|alt=Wikibooks logo]]<span>[https://www.wikibooks.org/ Wikibooks]<br/><small style="color:#555;">Free textbooks and manuals</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikidata-logo.svg|47px|link=https://www.wikidata.org/|alt=Wikidata logo]]<span>[https://www.wikidata.org/ Wikidata]<br/><small style="color:#555;">Free knowledge base</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikinews-logo.svg|51px|link=https://www.wikinews.org/|alt=Wikinews logo]]<span>[https://www.wikinews.org/ Wikinews]<br/><small style="color:#555;">Free-content news</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikiquote-logo.svg|35px|link=https://www.wikiquote.org/|alt=Wikiquote logo]]<span>[https://www.wikiquote.org/ Wikiquote]<br/><small style="color:#555;">Collection of quotations</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikisource-logo.svg|35px|link=https://www.wikisource.org/|alt=Wikisource logo]]<span>[https://www.wikisource.org/ Wikisource]<br/><small style="color:#555;">Free-content library</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikispecies-logo.svg|35px|link=https://species.wikimedia.org/|alt=Wikispecies logo]]<span>[https://species.wikimedia.org/ Wikispecies]<br/><small style="color:#555;">Directory of species</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikiversity logo 2017.svg|41px|link=https://www.wikiversity.org/|alt=Wikiversity logo]]<span>[https://www.wikiversity.org/ Wikiversity]<br/><small style="color:#555;">Free learning tools</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wikivoyage-Logo-v3-icon.svg|35px|link=https://www.wikivoyage.org/|alt=Wikivoyage logo]]<span>[https://www.wikivoyage.org/ Wikivoyage]<br/><small style="color:#555;">Free travel guide</small></span></li> <li style="display:flex;align-items:center;gap:16px;padding:4px 0;">[[File:Wiktionary-logo-v2.svg|35px|link=https://www.wiktionary.org/|alt=Wiktionary logo]]<span>[https://www.wiktionary.org/ Wiktionary]<br/><small style="color:#555;">Dictionary and thesaurus</small></span></li> </ul> </div> </div> <noinclude>[[Category:Starter kit templates]]</noinclude> sqqnlmlgl2eejn6h1edrw231bhol386 User:Alhassan Mohammed Awal/Starter kit/Wikipedia languages 2 178210 760298 2026-09-04T17:39:03Z Alhassan Mohammed Awal 59325 Initialised by StarterKit tool — ready for translation 760298 wikitext text/x-wiki <div style="border:1px solid #CBD5E1;border-radius:4px;background:#ffffff;overflow:hidden;"> <div style="background:#F8FAFC;border-bottom:1px solid #CBD5E1;padding:8px 16px;font-weight:bold;">Wikipedia languages</div> <div style="padding:16px;"> Many [https://meta.wikimedia.org/wiki/List_of_Wikipedias other Wikipedias are available]; some of the largest are listed below. <ul style="list-style:none;margin:8px 0 0 0;padding:0;"> <li style="margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;"> <div style="flex:1;height:1px;background:#CBD5E1;"></div> <div style="font-weight:bold;white-space:nowrap;">1,000,000+ articles</div> <div style="flex:1;height:1px;background:#CBD5E1;"></div> </div> <div style="line-height:2;"> [https://ar.wikipedia.org/wiki/ العربية] &nbsp;·&nbsp; [https://de.wikipedia.org/wiki/ Deutsch] &nbsp;·&nbsp; [https://es.wikipedia.org/wiki/ Español] &nbsp;·&nbsp; [https://fa.wikipedia.org/wiki/ فارسی]&lrm; · [https://fr.wikipedia.org/wiki/ Français] &nbsp;·&nbsp; [https://it.wikipedia.org/wiki/ Italiano] &nbsp;·&nbsp; [https://nl.wikipedia.org/wiki/ Nederlands] &nbsp;·&nbsp; [https://ja.wikipedia.org/wiki/ 日本語] &nbsp;·&nbsp; [https://pl.wikipedia.org/wiki/ Polski] &nbsp;·&nbsp; [https://pt.wikipedia.org/wiki/ Português] &nbsp;·&nbsp; [https://ru.wikipedia.org/wiki/ Русский] &nbsp;·&nbsp; [https://sv.wikipedia.org/wiki/ Svenska] &nbsp;·&nbsp; [https://uk.wikipedia.org/wiki/ Українська] &nbsp;·&nbsp; [https://vi.wikipedia.org/wiki/ Tiếng Việt] &nbsp;·&nbsp; [https://zh.wikipedia.org/wiki/ 中文] </div> </li> <li style="margin-bottom:12px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;"> <div style="flex:1;height:1px;background:#CBD5E1;"></div> <div style="font-weight:bold;white-space:nowrap;">250,000+ articles</div> <div style="flex:1;height:1px;background:#CBD5E1;"></div> </div> <div style="line-height:2;"> [https://id.wikipedia.org/wiki/ Bahasa Indonesia] &nbsp;·&nbsp; [https://ms.wikipedia.org/wiki/ Bahasa Melayu] &nbsp;·&nbsp; [https://nan.wikipedia.org/wiki/ 閩南語] &nbsp;·&nbsp; [https://bg.wikipedia.org/wiki/ Български] ·&nbsp; [https://ca.wikipedia.org/wiki/ Català] &nbsp;·&nbsp; [https://cs.wikipedia.org/wiki/ Čeština] &nbsp;·&nbsp; [https://da.wikipedia.org/wiki/ Dansk] &nbsp;·&nbsp; [https://et.wikipedia.org/wiki/ Eesti] &nbsp;·&nbsp; [https://el.wikipedia.org/wiki/ Ελληνικά] &nbsp;·&nbsp; [https://eo.wikipedia.org/wiki/ Esperanto] &nbsp;·&nbsp; [https://eu.wikipedia.org/wiki/ Euskara] &nbsp;·&nbsp; [https://he.wikipedia.org/wiki/ עברית] &nbsp;·&nbsp; [https://hy.wikipedia.org/wiki/ Հայերեն] &nbsp;·&nbsp; [https://ko.wikipedia.org/wiki/ 한국어] &nbsp;·&nbsp; [https://hu.wikipedia.org/wiki/ Magyar] &nbsp;·&nbsp; [https://no.wikipedia.org/wiki/ Norsk] &nbsp;·&nbsp; [https://ro.wikipedia.org/wiki/ Română] &nbsp;·&nbsp; [https://simple.wikipedia.org/wiki/ Simple English] &nbsp;·&nbsp; [https://sk.wikipedia.org/wiki/ Slovenčina] &nbsp;·&nbsp; [https://sr.wikipedia.org/wiki/ Српски] &nbsp;·&nbsp; [https://sh.wikipedia.org/wiki/ Srpskohrvatski] &nbsp;·&nbsp; [https://fi.wikipedia.org/wiki/ Suomi] &nbsp;·&nbsp; [https://tr.wikipedia.org/wiki/ Türkçe] &nbsp;·&nbsp; [https://uz.wikipedia.org/wiki/ Oʻzbek] </div> </li> <li style="margin-bottom:4px;"> <div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;"> <div style="flex:1;height:1px;background:#CBD5E1;"></div> <div style="font-weight:bold;white-space:nowrap;">50,000+ articles</div> <div style="flex:1;height:1px;background:#CBD5E1;"></div> </div> <div style="line-height:2;"> [https://ast.wikipedia.org/wiki/ Asturianu] &nbsp;·&nbsp; [https://az.wikipedia.org/wiki/ Azərbaycanca] &nbsp;·&nbsp; [https://bn.wikipedia.org/wiki/ বাংলা] &nbsp;·&nbsp; [https://bs.wikipedia.org/wiki/ Bosanski] &nbsp;·&nbsp; [https://ckb.wikipedia.org/wiki/ کوردی] &nbsp;·&nbsp; [https://fy.wikipedia.org/wiki/ Frysk] &nbsp;·&nbsp; [https://ga.wikipedia.org/wiki/ Gaeilge] &nbsp;·&nbsp; [https://gl.wikipedia.org/wiki/ Galego] &nbsp;·&nbsp; [https://hr.wikipedia.org/wiki/ Hrvatski] &nbsp;·&nbsp; [https://ka.wikipedia.org/wiki/ ქართული] &nbsp;·&nbsp; [https://ku.wikipedia.org/wiki/ Kurdî] &nbsp;·&nbsp; [https://lv.wikipedia.org/wiki/ Latviešu] &nbsp;·&nbsp; [https://lt.wikipedia.org/wiki/ Lietuvių] &nbsp;·&nbsp; [https://ml.wikipedia.org/wiki/ മലയാളം] &nbsp;·&nbsp; [https://mk.wikipedia.org/wiki/ Македонски] &nbsp;·&nbsp; [https://my.wikipedia.org/wiki/ မြန်မာဘာသာ] &nbsp;·&nbsp; [https://nn.wikipedia.org/wiki/ Norsk nynorsk] &nbsp;·&nbsp; [https://pa.wikipedia.org/wiki/ ਪੰਜਾਬੀ] &nbsp;·&nbsp; [https://sq.wikipedia.org/wiki/ Shqip] &nbsp;·&nbsp; [https://sl.wikipedia.org/wiki/ Slovenščina] &nbsp;·&nbsp; [https://th.wikipedia.org/wiki/ ไทย] &nbsp;·&nbsp; [https://te.wikipedia.org/wiki/ తెలుగు] &nbsp;·&nbsp; [https://ur.wikipedia.org/wiki/ اردو] </div> </li> </ul> </div> </div> <noinclude>[[Category:Starter kit templates]]</noinclude> qfil1o4vm3tqidfgmg9xjryqel5ppoz User:Oshwah/sandbox.js 2 178213 760354 2026-09-04T22:50:46Z Oshwah 26741 Add beta code to sandbox. 760354 javascript text/javascript //<nowiki> (function() { 'use strict'; mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (!operatorUsername || !targetUsername || (!isUserOrContributionsPage) || targetUsername === operatorUsername) { return; } // Add an action to the Tools menu. let link = mw.util.addPortletLink('p-tb','#','RevDel username','t-revdel-target-username','Hide ' + targetUsername + ' from their edits and log entries',null,'#t-userrights'); // Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','#','RevDel username','ca-revdel-target-username'); } if (!link) { return; } let anchor = link.querySelector('a') || link; let originalText = anchor.textContent; anchor.addEventListener('click',async function(event) { event.preventDefault(); if (running) { return; } // Recheck this immediately before performing anything destructive. if (!targetUsername || targetUsername === operatorUsername) { console.warn('Error (RevDel Username): Target user is not valid.'); mw.notify('No valid target user.', { type: 'error' } ); return; } running = true; try { displayStatus('Checking your permissions...'); let rights = await getRights(); if (!rights.has('deleterevision') || !rights.has('deletelogentry')) { throw new Error('Error: Your account does not have the \'deleterevision\' and \'deletelogentry\' rights required in order to use this script.'); } // Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (!reason || !reason.trim()) { return; } reason = reason.trim(); // Scan everything before changing anything. displayStatus('Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); let archivedByTitle = new Map(); if (rights.has('deletedhistory')) { displayStatus('Scanning deleted revisions...'); archivedByTitle = await getArchivedRevisionsByTitle(); } displayStatus('Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; // Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); } ).map(function(entry) { return entry.logid; } ); // Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); } ).map(function(entry) { return entry.logid; } ); archivedByTitle.forEach( function(ids) { archivedCount += ids.length; } ); let total = liveRevisionIds.length + archivedCount + logEntries.length; // Nothing to do or nothing was found. if (!total) { console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); return; } // Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + (rights.has('deletedhistory') ? '' : ' (Not scanned: You do not have \'deletedhistory\' permissions)') + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (!window.confirm(message)) { return; } // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,null,'user','Live revisions'); } // Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } // Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } // Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } // One final scan tells us whether anything visible remains. displayStatus('Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); if (rights.has('deletedhistory')) { remainingArchived = await getArchivedRevisionsByTitle(); } let remainingArchivedCount = 0; remainingArchived.forEach( function(ids) { remainingArchivedCount += ids.length; } ); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; // Completion logs and messages. console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('RevDel username script failed: ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; anchor.textContent = originalText; } }); }); }()); function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } // Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } // Get the user rights of the account running the script. async function getRights() { let api = new mw.Api(); let data = await api.get({ action: 'query', meta: 'userinfo', uiprop: 'rights', format: 'json', formatversion: 2 }); return new Set(data.query.userinfo.rights || []); } function displayStatus(text) { $('#mw-content-text').append('Status (RevDel Username): "' + text + '".'); console.info('Info (RevDel Username): "' + text + '".'); } // Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. // Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } // Find revisions by the target user belonging to pages which have themselves been deleted. // RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } // Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately // reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } // Perform one RevisionDelete request. // "hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. // suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } // Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } // Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> 9bpnryc2i57f9ljod2rkijk7wrm0d91 760355 760354 2026-09-04T22:53:55Z Oshwah 26741 Encapsulate <p> tags when displaying status. 760355 javascript text/javascript //<nowiki> (function() { 'use strict'; mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (!operatorUsername || !targetUsername || (!isUserOrContributionsPage) || targetUsername === operatorUsername) { return; } // Add an action to the Tools menu. let link = mw.util.addPortletLink('p-tb','#','RevDel username','t-revdel-target-username','Hide ' + targetUsername + ' from their edits and log entries',null,'#t-userrights'); // Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','#','RevDel username','ca-revdel-target-username'); } if (!link) { return; } let anchor = link.querySelector('a') || link; let originalText = anchor.textContent; anchor.addEventListener('click',async function(event) { event.preventDefault(); if (running) { return; } // Recheck this immediately before performing anything destructive. if (!targetUsername || targetUsername === operatorUsername) { console.warn('Error (RevDel Username): Target user is not valid.'); mw.notify('No valid target user.', { type: 'error' } ); return; } running = true; try { displayStatus('Checking your permissions...'); let rights = await getRights(); if (!rights.has('deleterevision') || !rights.has('deletelogentry')) { throw new Error('Error: Your account does not have the \'deleterevision\' and \'deletelogentry\' rights required in order to use this script.'); } // Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (!reason || !reason.trim()) { return; } reason = reason.trim(); // Scan everything before changing anything. displayStatus('Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); let archivedByTitle = new Map(); if (rights.has('deletedhistory')) { displayStatus('Scanning deleted revisions...'); archivedByTitle = await getArchivedRevisionsByTitle(); } displayStatus('Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; // Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); } ).map(function(entry) { return entry.logid; } ); // Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); } ).map(function(entry) { return entry.logid; } ); archivedByTitle.forEach( function(ids) { archivedCount += ids.length; } ); let total = liveRevisionIds.length + archivedCount + logEntries.length; // Nothing to do or nothing was found. if (!total) { console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); return; } // Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + (rights.has('deletedhistory') ? '' : ' (Not scanned: You do not have \'deletedhistory\' permissions)') + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (!window.confirm(message)) { return; } // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,null,'user','Live revisions'); } // Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } // Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } // Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } // One final scan tells us whether anything visible remains. displayStatus('Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); if (rights.has('deletedhistory')) { remainingArchived = await getArchivedRevisionsByTitle(); } let remainingArchivedCount = 0; remainingArchived.forEach( function(ids) { remainingArchivedCount += ids.length; } ); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; // Completion logs and messages. console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('RevDel username script failed: ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; anchor.textContent = originalText; } }); }); }()); function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } // Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } // Get the user rights of the account running the script. async function getRights() { let api = new mw.Api(); let data = await api.get({ action: 'query', meta: 'userinfo', uiprop: 'rights', format: 'json', formatversion: 2 }); return new Set(data.query.userinfo.rights || []); } function displayStatus(text) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Info (RevDel Username): "' + text + '".'); } // Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. // Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } // Find revisions by the target user belonging to pages which have themselves been deleted. // RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } // Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately // reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } // Perform one RevisionDelete request. // "hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. // suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } // Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } // Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> bgv4ii70ygj8qomyjbsiyh7cb3mq08i 760356 760355 2026-09-04T22:58:53Z Oshwah 26741 Add completion message with link to reload page. 760356 javascript text/javascript //<nowiki> (function() { 'use strict'; mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (!operatorUsername || !targetUsername || (!isUserOrContributionsPage) || targetUsername === operatorUsername) { return; } // Add an action to the Tools menu. let link = mw.util.addPortletLink('p-tb','#','RevDel username','t-revdel-target-username','Hide ' + targetUsername + ' from their edits and log entries',null,'#t-userrights'); // Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','#','RevDel username','ca-revdel-target-username'); } if (!link) { return; } let anchor = link.querySelector('a') || link; let originalText = anchor.textContent; anchor.addEventListener('click',async function(event) { event.preventDefault(); if (running) { return; } // Recheck this immediately before performing anything destructive. if (!targetUsername || targetUsername === operatorUsername) { console.warn('Error (RevDel Username): Target user is not valid.'); mw.notify('No valid target user.', { type: 'error' } ); return; } running = true; try { displayStatus('Checking your permissions...'); let rights = await getRights(); if (!rights.has('deleterevision') || !rights.has('deletelogentry')) { throw new Error('Error: Your account does not have the \'deleterevision\' and \'deletelogentry\' rights required in order to use this script.'); } // Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (!reason || !reason.trim()) { return; } reason = reason.trim(); // Scan everything before changing anything. displayStatus('Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); let archivedByTitle = new Map(); if (rights.has('deletedhistory')) { displayStatus('Scanning deleted revisions...'); archivedByTitle = await getArchivedRevisionsByTitle(); } displayStatus('Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; // Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); } ).map(function(entry) { return entry.logid; } ); // Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); } ).map(function(entry) { return entry.logid; } ); archivedByTitle.forEach( function(ids) { archivedCount += ids.length; } ); let total = liveRevisionIds.length + archivedCount + logEntries.length; // Nothing to do or nothing was found. if (!total) { console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); return; } // Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + (rights.has('deletedhistory') ? '' : ' (Not scanned: You do not have \'deletedhistory\' permissions)') + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (!window.confirm(message)) { return; } // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,null,'user','Live revisions'); } // Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } // Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } // Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } // One final scan tells us whether anything visible remains. displayStatus('Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); if (rights.has('deletedhistory')) { remainingArchived = await getArchivedRevisionsByTitle(); } let remainingArchivedCount = 0; remainingArchived.forEach( function(ids) { remainingArchivedCount += ids.length; } ); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; // Completion logs and messages. console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('RevDel username script failed: ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; anchor.textContent = originalText; } }); }); }()); function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } // Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } // Get the user rights of the account running the script. async function getRights() { let api = new mw.Api(); let data = await api.get({ action: 'query', meta: 'userinfo', uiprop: 'rights', format: 'json', formatversion: 2 }); return new Set(data.query.userinfo.rights || []); } function displayStatus(text) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Info (RevDel Username): "' + text + '".'); } // Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. // Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } // Find revisions by the target user belonging to pages which have themselves been deleted. // RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } // Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately // reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } // Perform one RevisionDelete request. // "hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. // suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } // Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } // Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> ryz1gbxvbkxlm6ojun5swoed64br7wr 760357 760356 2026-09-04T23:43:28Z Oshwah 26741 Refactored code flow and main functions. 760357 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { // Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; // Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { //let anchor = link.querySelector('a') || link; //let originalText = anchor.textContent; $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { // Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (reason && reason.trim()) { reason = reason.trim(); // Scan everything before changing anything. displayStatus('Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); let archivedByTitle = new Map(); displayStatus('Scanning deleted revisions...'); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus('Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; // Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); // Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; // Nothing to do or nothing was found. if (total) { // Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (!window.confirm(message)) { return; } // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,null,'user','Live revisions'); } // Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } // Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } // Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } // One final scan tells us whether anything visible remains. displayStatus('Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; // Completion logs and messages. //console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { // No revisions to redact were found. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; //anchor.textContent = originalText; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } // Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(text) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Info (RevDel Username): "' + text + '".'); } // Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. // Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } // Find revisions by the target user belonging to pages which have themselves been deleted. // RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } // Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately // reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } // Perform one RevisionDelete request. // "hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. // suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } // Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } // Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> erql0carp4mo1otkonsera60uxjxonh 760361 760357 2026-09-04T23:57:49Z Oshwah 26741 Add message when user aborts. 760361 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { // Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; // Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { //let anchor = link.querySelector('a') || link; //let originalText = anchor.textContent; $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { // Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (reason && reason.trim()) { reason = reason.trim(); // Scan everything before changing anything. displayStatus('Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); let archivedByTitle = new Map(); displayStatus('Scanning deleted revisions...'); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus('Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; // Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); // Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; // Nothing to do or nothing was found. if (total) { // Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,null,'user','Live revisions'); } // Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } // Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } // Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } // One final scan tells us whether anything visible remains. displayStatus('Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; // Completion logs and messages. //console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { // No revisions to redact were found. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; //anchor.textContent = originalText; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } // Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(text) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Info (RevDel Username): "' + text + '".'); } // Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. // Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } // Find revisions by the target user belonging to pages which have themselves been deleted. // RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } // Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately // reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } // Perform one RevisionDelete request. // "hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. // suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } // Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } // Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> cpw5roqfa3jblkd555rdimpwps9p5mu 760362 760361 2026-09-04T23:59:04Z Oshwah 26741 Add message when user enters no reason or cancels. 760362 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { // Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; // Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { //let anchor = link.querySelector('a') || link; //let originalText = anchor.textContent; $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { // Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (reason && reason.trim()) { reason = reason.trim(); // Scan everything before changing anything. displayStatus('Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); let archivedByTitle = new Map(); displayStatus('Scanning deleted revisions...'); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus('Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; // Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); // Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; // Nothing to do or nothing was found. if (total) { // Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,null,'user','Live revisions'); } // Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } // Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } // Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } // One final scan tells us whether anything visible remains. displayStatus('Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; // Completion logs and messages. //console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { // No revisions to redact were found. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } else { $('#mw-content-text').append('<p><b>Aborted (no reason entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; //anchor.textContent = originalText; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } // Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(text) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Info (RevDel Username): "' + text + '".'); } // Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. // Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } // Find revisions by the target user belonging to pages which have themselves been deleted. // RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } // Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately // reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } // Perform one RevisionDelete request. // "hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. // suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } // Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } // Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> kzee1s56mghxn3fa22h0r8eee4jz51m 760363 760362 2026-09-05T00:00:00Z Oshwah 26741 Append to message. 760363 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { // Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; // Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { //let anchor = link.querySelector('a') || link; //let originalText = anchor.textContent; $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { // Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (reason && reason.trim()) { reason = reason.trim(); // Scan everything before changing anything. displayStatus('Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); let archivedByTitle = new Map(); displayStatus('Scanning deleted revisions...'); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus('Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; // Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); // Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; // Nothing to do or nothing was found. if (total) { // Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,null,'user','Live revisions'); } // Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } // Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } // Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } // One final scan tells us whether anything visible remains. displayStatus('Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; // Completion logs and messages. //console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { // No revisions to redact were found. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; //anchor.textContent = originalText; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } // Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(text) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Info (RevDel Username): "' + text + '".'); } // Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. // Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } // Find revisions by the target user belonging to pages which have themselves been deleted. // RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } // Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately // reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } // Perform one RevisionDelete request. // "hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. // suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } // Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } // Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> kvdjgip9re9ozdc28mlqwkbzte99qjj 760364 760363 2026-09-05T00:03:01Z Oshwah 26741 Comment text, formatting. 760364 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { //let anchor = link.querySelector('a') || link; //let originalText = anchor.textContent; $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { //Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (reason && reason.trim()) { reason = reason.trim(); //Scan everything before changing anything. displayStatus('Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); let archivedByTitle = new Map(); displayStatus('Scanning deleted revisions...'); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus('Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,null,'user','Live revisions'); } //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus('Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. //console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } else { $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; //anchor.textContent = originalText; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(text) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Info (RevDel Username): "' + text + '".'); } //Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. //Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have themselves been deleted. //RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately //reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform one RevisionDelete request. //"hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. //suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> b0124vht9v14hhu98g57ekt7tn4ayy2 760367 760364 2026-09-05T01:27:31Z Oshwah 26741 Segregated and parsed code into functions. 760367 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { //Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (reason && reason.trim()) { reason = reason.trim(); if (countAndConfirm(reason)) { performRevDelOperation(); } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); //let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. //console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. //Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have themselves been deleted. //RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately //reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform one RevisionDelete request. //"hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. //suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus((description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> ciletn0k9lp3553wk8vhm8tp4j10965 760368 760367 2026-09-05T01:34:40Z Oshwah 26741 Fix displayStatus function call. 760368 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { //Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (reason && reason.trim()) { reason = reason.trim(); if (countAndConfirm(reason)) { performRevDelOperation(); } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); //let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. //console.info('Operation complete for ' + targetUsername + ' (' + liveRevisionIds.length + 'live revision(s), ' + archivedCount + ' deleted revision(s), ' + (newUsersLogIds.length + otherLogIds.length) + ' logs); ' + remainingTotal + ' logs remain.'); mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. //Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have themselves been deleted. //RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately //reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform one RevisionDelete request. //"hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. //suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> fz7olp3257s3sso7ppsnd5xyobykpvg 760369 760368 2026-09-05T01:41:02Z Oshwah 26741 Added missing function call to revdel username from live revisions. 760369 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { //Prompt user to enter reason for RevDel. let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); if (reason && reason.trim()) { reason = reason.trim(); if (countAndConfirm(reason)) { performRevDelOperation(); } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); //let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,status,null,'user','Live revisions'); } //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. //Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have themselves been deleted. //RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately //reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform one RevisionDelete request. //"hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. //suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> kmjc5z9thpba23tocog34a0a4facjo2 760370 760369 2026-09-05T01:44:56Z Oshwah 26741 Re-arrange comments. 760370 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); if (countAndConfirm(reason)) { //Count all of the edits and logs where the username is currently visible, confirm that the user wishes to proceed. performRevDelOperation(); //Perform redaction operation, show results to user. } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); //let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,status,null,'user','Live revisions'); } //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. //Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have themselves been deleted. //RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately //reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform one RevisionDelete request. //"hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. //suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> m090cuqq1onob0b8shc1t4hiue557h2 760371 760370 2026-09-05T01:48:04Z Oshwah 26741 Add status message when no eligible revisions found. 760371 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); if (countAndConfirm(reason)) { //Count all of the edits and logs where the username is currently visible, confirm that the user wishes to proceed. performRevDelOperation(); //Perform redaction operation, show results to user. } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); //let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,status,null,'user','Live revisions'); } //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. //Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have themselves been deleted. //RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately //reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform one RevisionDelete request. //"hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. //suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> cyqonb2vzybqppa3kmnpnhflq5bz96m 760372 760371 2026-09-05T01:52:51Z Oshwah 26741 Fix error handling. 760372 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); if (countAndConfirm(reason)) { //Count all of the edits and logs where the username is currently visible, confirm that the user wishes to proceed. performRevDelOperation(); //Perform redaction operation, show results to user. } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); //let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,status,null,'user','Live revisions'); } //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. //Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have themselves been deleted. //RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately //reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform one RevisionDelete request. //"hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. //suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> ifjqpvv04l4use3zbz2ru0rnm3zpikb 760373 760372 2026-09-05T01:57:45Z Oshwah 26741 Fix abort status message location, duplicate function call. 760373 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(function() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); countAndConfirm(reason); } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } }); function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. userConfirmed = true; performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } return userConfirmed; } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,status,null,'user','Live revisions'); } //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the user whose User/User talk/Contributions page is being viewed. //Requesting the user property causes already-hidden usernames to be marked with userhidden, allowing those revisions to be skipped. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have themselves been deleted. //RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately //reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform one RevisionDelete request. //"hide" determines which fields are RevDeleted: 'user' - Hide only the user. 'content|user' - Hide the action/content and user. //suppress=nochange means the script does not alter the existing suppression status of the item. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> 6xg6xjvz6ucyhzpfyrucci0hdvktyx2 760374 760373 2026-09-05T02:03:50Z Oshwah 26741 Moved starting function into separate init function, cleaned up comments. 760374 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(init_rdu) function init_rdu() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); countAndConfirm(reason); } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. userConfirmed = true; performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } return userConfirmed; } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,status,null,'user','Live revisions'); } //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the target user. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have been deleted. RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform a RevisionDelete request. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus('Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> bnadqnl4ku99vcxnh4m717prug1t03x 760375 760374 2026-09-05T02:05:44Z Oshwah 26741 Fix displayStatus function call. 760375 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(init_rdu) function init_rdu() { const isUserAuthorized = queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); countAndConfirm(reason); } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } } function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }).then(function(data) { //Parse the list of the user's global group memberships from the returned data, assign it to globalUserGroups. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. if (allowedGlobalGroups.indexOf(globalUserGroups) > -1) { isAuthorized = true; } } else { throw new Error('"data.query" does not exist.'); } }).catch(function(error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; }); } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: Parsing API call data for global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (QuickBlockScript): Parsing API call data for global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionIds = await getLiveRevisionIds(); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = new Map(); archivedByTitle = await getArchivedRevisionsByTitle(); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); let archivedCount = 0; //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds =logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); archivedByTitle.forEach(function(ids) { archivedCount += ids.length; }); let total = liveRevisionIds.length + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionIds.length + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. userConfirmed = true; performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } return userConfirmed; } async function performRevDelOperation(liveRevisionIds,newUsersLogIds,otherLogIds,archivedCount,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); // Process normal live revisions. if (liveRevisionIds.length) { await hideIdList('revision',liveRevisionIds,reason,status,null,'user','Live revisions'); } //Process revisions belonging to deleted pages. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entry. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getLiveRevisionIds(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = new Map(); remainingArchived = await getArchivedRevisionsByTitle(); let remainingArchivedCount = 0; remainingArchived.forEach(function(ids) { remainingArchivedCount += ids.length; }); let remainingTotal = remainingLive.length + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionIds.length + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the target user. async function getLiveRevisionIds() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); return unique(rows.filter(function(row) { return (row.revid && !hasOwn(row,'userhidden')); }).map(function(row) { return row.revid; }) ); } //Find revisions by the target user belonging to pages which have been deleted. RevisionDelete type=archive requires a target page title, so revision IDs are grouped by title. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let ids = byTitle.get(page.title) || []; (page.revisions || []).forEach( function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { ids.push(revision.revid); } } ); if (ids.length) { byTitle.set(page.title,unique(ids)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row, 'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform a RevisionDelete request. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary revision/log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Archived revisions must be processed separately for each deleted page title. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title); } } } //</nowiki> b684hqp9h2na4t813p9r2x65p8ovs0s 760376 760375 2026-09-05T02:24:15Z Oshwah 26741 Add fixes to test. 760376 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(init_rdu).catch(function(error) { console.error('Error (RevDel Username): Initialization failed.',error); }); async function init_rdu() { const isUserAuthorized = await queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); await countAndConfirm(reason); } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); const data = await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }); //Parse the list of the user's global group memberships from the returned data. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. isAuthorized = globalUserGroups.some(function(group) { return allowedGlobalGroups.includes(group); }); } else { throw new Error('"data.query" does not exist.'); } } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (RevDel Username): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } function countMapValues(map) { let count = 0; map.forEach(function(values) { count += values.length; }); return count; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionsByTitle = await getVisibleLiveRevisionsByTitle(); let liveRevisionCount = countMapValues(liveRevisionsByTitle); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = await getArchivedRevisionsByTitle(); let archivedCount = countMapValues(archivedByTitle); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds = logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); let total = liveRevisionCount + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionCount + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. userConfirmed = true; await performRevDelOperation(liveRevisionsByTitle,newUsersLogIds,otherLogIds,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } return userConfirmed; } async function performRevDelOperation(liveRevisionsByTitle,newUsersLogIds,otherLogIds,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); const liveRevisionCount = countMapValues(liveRevisionsByTitle); const archivedCount = countMapValues(archivedByTitle); //Process normal live revisions. Revisions must be grouped by page title because RevisionDelete operates on one page at a time. if (liveRevisionCount) { await hideLiveRevisions(liveRevisionsByTitle,reason); } //Process revisions belonging to deleted pages. Archive RevisionDelete identifies revisions by timestamp, not revision ID. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entries. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getVisibleLiveRevisionsByTitle(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = await getArchivedRevisionsByTitle(); let remainingLiveCount = countMapValues(remainingLive); let remainingArchivedCount = countMapValues(remainingArchived); let remainingTotal = remainingLiveCount + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionCount + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the target user, group them by page title, and verify that the username is still publicly visible. async function getVisibleLiveRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); let titleByRevisionId = new Map(); rows.forEach(function(row) { if (row.revid) { titleByRevisionId.set(row.revid,row.title); } }); let revisionIds = Array.from(titleByRevisionId.keys()); let visibleRevisionIds = await getVisibleLiveRevisionIds(revisionIds); let byTitle = new Map(); visibleRevisionIds.forEach(function(revid) { let title = titleByRevisionId.get(revid); if (title) { let ids = byTitle.get(title) || []; ids.push(revid); byTitle.set(title,unique(ids)); } }); return byTitle; } //Check live revision IDs with prop=revisions because usercontribs does not expose the userhidden flag. async function getVisibleLiveRevisionIds(revisionIds) { const BATCH_SIZE = 50; let api = new mw.Api(); let visibleRevisionIds = []; let batches = chunks(revisionIds,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { let data = await api.get({ action: 'query', prop: 'revisions', revids: batches[i].join('|'), rvprop: 'ids|user', format: 'json', formatversion: 2, maxlag: 5 }); let pages = (data.query && data.query.pages) || []; pages.forEach(function(page) { (page.revisions || []).forEach(function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { visibleRevisionIds.push(revision.revid); } }); }); } return unique(visibleRevisionIds); } //Find revisions by the target user belonging to pages which have been deleted. RevisionDelete type=archive requires a target page title and uses the archived revision timestamp as the ID. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|timestamp|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let timestamps = byTitle.get(page.title) || []; (page.revisions || []).forEach(function(revision) { if (revision.timestamp && !hasOwn(revision,'userhidden')) { timestamps.push(revision.timestamp); } }); if (timestamps.length) { byTitle.set(page.title,unique(timestamps)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row,'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform a RevisionDelete request. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Live revisions must be processed separately for each page title. async function hideLiveRevisions(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Live edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('revision',batches[j],reason,title,'user'); } } } //Archived revisions must be processed separately for each deleted page title. For type=archive, the IDs are archive timestamps. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let timestamps = entries[i][1]; let batches = chunks(timestamps,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title,'user'); } } } //</nowiki> 6y4h01a9gbesg3q34vp7pj0mtbwe5dn 760378 760376 2026-09-05T02:31:11Z Oshwah 26741 Flip API request to undo hiding username. (TEST) 760378 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(init_rdu).catch(function(error) { console.error('Error (RevDel Username): Initialization failed.',error); }); async function init_rdu() { const isUserAuthorized = await queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); await countAndConfirm(reason); } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); const data = await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }); //Parse the list of the user's global group memberships from the returned data. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. isAuthorized = globalUserGroups.some(function(group) { return allowedGlobalGroups.includes(group); }); } else { throw new Error('"data.query" does not exist.'); } } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (RevDel Username): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } function countMapValues(map) { let count = 0; map.forEach(function(values) { count += values.length; }); return count; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionsByTitle = await getVisibleLiveRevisionsByTitle(); let liveRevisionCount = countMapValues(liveRevisionsByTitle); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = await getArchivedRevisionsByTitle(); let archivedCount = countMapValues(archivedByTitle); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds = logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); let total = liveRevisionCount + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionCount + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. userConfirmed = true; await performRevDelOperation(liveRevisionsByTitle,newUsersLogIds,otherLogIds,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } return userConfirmed; } async function performRevDelOperation(liveRevisionsByTitle,newUsersLogIds,otherLogIds,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); const liveRevisionCount = countMapValues(liveRevisionsByTitle); const archivedCount = countMapValues(archivedByTitle); //Process normal live revisions. Revisions must be grouped by page title because RevisionDelete operates on one page at a time. if (liveRevisionCount) { await hideLiveRevisions(liveRevisionsByTitle,reason); } //Process revisions belonging to deleted pages. Archive RevisionDelete identifies revisions by timestamp, not revision ID. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entries. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getVisibleLiveRevisionsByTitle(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = await getArchivedRevisionsByTitle(); let remainingLiveCount = countMapValues(remainingLive); let remainingArchivedCount = countMapValues(remainingArchived); let remainingTotal = remainingLiveCount + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionCount + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the target user, group them by page title, and verify that the username is still publicly visible. async function getVisibleLiveRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); let titleByRevisionId = new Map(); rows.forEach(function(row) { if (row.revid) { titleByRevisionId.set(row.revid,row.title); } }); let revisionIds = Array.from(titleByRevisionId.keys()); let visibleRevisionIds = await getVisibleLiveRevisionIds(revisionIds); let byTitle = new Map(); visibleRevisionIds.forEach(function(revid) { let title = titleByRevisionId.get(revid); if (title) { let ids = byTitle.get(title) || []; ids.push(revid); byTitle.set(title,unique(ids)); } }); return byTitle; } //Check live revision IDs with prop=revisions because usercontribs does not expose the userhidden flag. async function getVisibleLiveRevisionIds(revisionIds) { const BATCH_SIZE = 50; let api = new mw.Api(); let visibleRevisionIds = []; let batches = chunks(revisionIds,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { let data = await api.get({ action: 'query', prop: 'revisions', revids: batches[i].join('|'), rvprop: 'ids|user', format: 'json', formatversion: 2, maxlag: 5 }); let pages = (data.query && data.query.pages) || []; pages.forEach(function(page) { (page.revisions || []).forEach(function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { visibleRevisionIds.push(revision.revid); } }); }); } return unique(visibleRevisionIds); } //Find revisions by the target user belonging to pages which have been deleted. RevisionDelete type=archive requires a target page title and uses the archived revision timestamp as the ID. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|timestamp|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let timestamps = byTitle.get(page.title) || []; (page.revisions || []).forEach(function(revision) { if (revision.timestamp && !hasOwn(revision,'userhidden')) { timestamps.push(revision.timestamp); } }); if (timestamps.length) { byTitle.set(page.title,unique(timestamps)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row,'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform a RevisionDelete request. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), //hide: hide || 'user', show: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Live revisions must be processed separately for each page title. async function hideLiveRevisions(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Live edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('revision',batches[j],reason,title,'user'); } } } //Archived revisions must be processed separately for each deleted page title. For type=archive, the IDs are archive timestamps. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let timestamps = entries[i][1]; let batches = chunks(timestamps,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title,'user'); } } } //</nowiki> hxuiipaqepuujs2ncmkgra1rzx1ludu 760379 760378 2026-09-05T02:33:24Z Oshwah 26741 Roll back. 760379 javascript text/javascript //<nowiki> mw.loader.using(['mediawiki.api','mediawiki.util']).then(init_rdu).catch(function(error) { console.error('Error (RevDel Username): Initialization failed.',error); }); async function init_rdu() { const isUserAuthorized = await queryUserAuthorization(); const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); const namespaceNumber = mw.config.get('wgNamespaceNumber'); const canonicalSpecialPage = mw.config.get('wgCanonicalSpecialPageName'); const isUserOrContributionsPage = namespaceNumber === 2 || namespaceNumber === 3 || canonicalSpecialPage === 'Contributions'; let running = false; if (isUserAuthorized && operatorUsername && targetUsername && isUserOrContributionsPage && operatorUsername !== targetUsername) { //Add link to the Tools menu. let link = mw.util.addPortletLink('p-tb','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.',null,'#t-userrights'); let isSkinCompatible = false; //Fallback for skins which don't expose p-tb. if (!link) { link = mw.util.addPortletLink('p-cactions','javascript:void(0)','RevDel username','osh-revdel-target-username','Redact this account\'s username from ALL edits and logs.'); if (!link) { console.warn('Error (RevDel Username): Incompatible skin used. Cannot add portlet link. Script will not continue...'); } else { isSkinCompatible = true; } } else { isSkinCompatible = true; } if (isSkinCompatible) { $('#osh-revdel-target-username').on('click',async function(event) { event.preventDefault(); if (!running) { running = true; try { let reason = window.prompt('Enter the RevDel reason for ' + targetUsername + '.\n(Leave blank and press \'Enter\' to cancel): '); //Prompt user to enter reason for RevDel. if (reason && reason.trim()) { reason = reason.trim(); await countAndConfirm(reason); } else { $('#mw-content-text').append('<p><b>Aborted (user cancelled or no reason was entered) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } catch (error) { console.error('Error (RevDel Username): ',error); mw.notify('Error (RevDel Username): ' + (error && error.message ? error.message : error), { type: 'error', autoHide: false } ); } finally { running = false; } } }); } } } async function queryUserAuthorization() { const allowedGlobalGroups = ['steward']; let isAuthorized = mw.config.get('wgUserGroups').includes('sysop'); if (!isAuthorized) { //User is not a member of one of the required local user groups. Check to see if they have global group permissions. try { const api = new mw.Api(); const data = await api.get({ //Query API for global groups. action: 'query', meta: 'globaluserinfo', guiprop: 'groups', format: 'json', formatversion: 2 }); //Parse the list of the user's global group memberships from the returned data. if (data && data.query) { const globalUserGroups = data.query.globaluserinfo.groups || []; //Check if the user is a member of any of the required global groups. isAuthorized = globalUserGroups.some(function(group) { return allowedGlobalGroups.includes(group); }); } else { throw new Error('"data.query" does not exist.'); } } catch (error) { $('#mw-content-text').append('<p><b>Error</b>: API call to retrieve global group memberships failed. Error details: <i>' + error + '</i>.</p>'); console.warn('Error (RevDel Username): API call to retrieve global group memberships failed. Error details: ' + error + '.'); return false; } } return isAuthorized; } function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj,key); } function unique(values) { return Array.from(new Set(values)); } function chunks(values, size) { let result = []; for (let i = 0; i < values.length; i += size) { result.push(values.slice(i, i + size)); } return result; } function countMapValues(map) { let count = 0; map.forEach(function(values) { count += values.length; }); return count; } //Perform a continued API query until all results have been retrieved. async function queryAll(params, extractor) { let api = new mw.Api(); let results = []; let cont = {}; do { let data = await api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, maxlag: 5 }, params, cont ) ); results = results.concat(extractor(data)); cont = data.continue || null; } while (cont); return results; } function displayStatus(status,text) { if (!status) { $('#mw-content-text').append('<p>Status (RevDel Username): "' + text + '".</p>'); console.info('Status (RevDel Username): "' + text + '".'); } else { $('#mw-content-text').append('<p><b>' + status + '</b> ' + text + '.</p>'); } } async function countAndConfirm(reason) { const operatorUsername = mw.config.get('wgUserName'); const targetUsername = mw.config.get('wgRelevantUserName'); let userConfirmed = false; //Scan everything before changing anything. displayStatus(null,'Scanning live revisions...'); let liveRevisionsByTitle = await getVisibleLiveRevisionsByTitle(); let liveRevisionCount = countMapValues(liveRevisionsByTitle); displayStatus(null,'Scanning deleted revisions...'); let archivedByTitle = await getArchivedRevisionsByTitle(); let archivedCount = countMapValues(archivedByTitle); displayStatus(null,'Scanning logs...'); let logEntries = await getVisibleLogEntries(); //Copy user creation log entries to newUsersLogIds. let newUsersLogIds = logEntries.filter(function(entry) { return (entry.type === 'newusers'); }).map(function(entry) { return entry.logid; }); //Copy all other log entries to otherLogIds. let otherLogIds = logEntries.filter(function(entry) { return (entry.type !== 'newusers'); }).map(function(entry) { return entry.logid; }); let total = liveRevisionCount + archivedCount + logEntries.length; if (total) { //Edits or logs with username publicly visible were found. //Show exactly what will be changed. let message = 'Target account: ' + targetUsername + '\nOperator: ' + operatorUsername + '\n\nThe following will be RevisionDeleted:' + '\n\nLive revisions: ' + liveRevisionCount + '\nDeleted revisions: ' + archivedCount + '\nUser creation logs: ' + newUsersLogIds.length + ' (action + username)' + '\nAll other logs: ' + otherLogIds.length + ' (username only)' + '\nTotal log entries: ' + (otherLogIds.length + newUsersLogIds.length) +'\n\nTOTAL EDITS AND LOGS: ' + total + '\n\nDo you wish to continue the operation?'; if (window.confirm(message)) { //User has confirmed that they wish to continue. userConfirmed = true; await performRevDelOperation(liveRevisionsByTitle,newUsersLogIds,otherLogIds,archivedByTitle,reason); } else { //Confirmation message box cancelled by user. $('#mw-content-text').append('<p><b>Aborted by user (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } } else { //No revisions or logs with username publicly visible were found to redact. console.info('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.'); $('#mw-content-text').append('<p><b>Aborted (no edits or logs with the username publicly visible were found) (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); mw.notify('Info (RevDel Username): No edits or logs with the username visible were found for ' + targetUsername + '.', { type: 'info' } ); } return userConfirmed; } async function performRevDelOperation(liveRevisionsByTitle,newUsersLogIds,otherLogIds,archivedByTitle,reason) { const targetUsername = mw.config.get('wgRelevantUserName'); const liveRevisionCount = countMapValues(liveRevisionsByTitle); const archivedCount = countMapValues(archivedByTitle); //Process normal live revisions. Revisions must be grouped by page title because RevisionDelete operates on one page at a time. if (liveRevisionCount) { await hideLiveRevisions(liveRevisionsByTitle,reason); } //Process revisions belonging to deleted pages. Archive RevisionDelete identifies revisions by timestamp, not revision ID. if (archivedCount) { await hideArchived(archivedByTitle,reason); } //Process log entries that are not account creations. if (otherLogIds.length) { await hideIdList('logging',otherLogIds,reason,null,'user','All other logs'); } //Process account creation log entries. if (newUsersLogIds.length) { await hideIdList('logging',newUsersLogIds,reason,null,'content|user','User creation logs'); } //One final scan tells us whether anything visible remains. displayStatus(null,'Verifying results...'); let remainingLive = await getVisibleLiveRevisionsByTitle(); let remainingLogs = await getVisibleLogEntries(); let remainingArchived = await getArchivedRevisionsByTitle(); let remainingLiveCount = countMapValues(remainingLive); let remainingArchivedCount = countMapValues(remainingArchived); let remainingTotal = remainingLiveCount + remainingArchivedCount + remainingLogs.length; //Completion logs and messages. mw.notify('Finished processing ' + targetUsername + '. Username hidden on ' + liveRevisionCount + ' live revision(s), ' + archivedCount + ' deleted revision(s), and ' + otherLogIds.length + ' logs. Action + username hidden on ' + newUsersLogIds.length + ' user creation logs. There are ' + remainingTotal + ' revisions or logs with the username still visible.', { type: remainingTotal ? 'warn' : 'success', autoHide: false } ); $('#mw-content-text').append('<p><b>Complete (<a href="javascript:document.location.reload()">reload page</a>)</b>.</p>'); } //Find live revisions attributed to the target user, group them by page title, and verify that the username is still publicly visible. async function getVisibleLiveRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll({ list: 'usercontribs', ucuser: targetUsername, ucprop: 'ids|title', uclimit: 'max' }, function(data) { return (data.query && data.query.usercontribs) || []; } ); let titleByRevisionId = new Map(); rows.forEach(function(row) { if (row.revid) { titleByRevisionId.set(row.revid,row.title); } }); let revisionIds = Array.from(titleByRevisionId.keys()); let visibleRevisionIds = await getVisibleLiveRevisionIds(revisionIds); let byTitle = new Map(); visibleRevisionIds.forEach(function(revid) { let title = titleByRevisionId.get(revid); if (title) { let ids = byTitle.get(title) || []; ids.push(revid); byTitle.set(title,unique(ids)); } }); return byTitle; } //Check live revision IDs with prop=revisions because usercontribs does not expose the userhidden flag. async function getVisibleLiveRevisionIds(revisionIds) { const BATCH_SIZE = 50; let api = new mw.Api(); let visibleRevisionIds = []; let batches = chunks(revisionIds,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { let data = await api.get({ action: 'query', prop: 'revisions', revids: batches[i].join('|'), rvprop: 'ids|user', format: 'json', formatversion: 2, maxlag: 5 }); let pages = (data.query && data.query.pages) || []; pages.forEach(function(page) { (page.revisions || []).forEach(function(revision) { if (revision.revid && !hasOwn(revision,'userhidden')) { visibleRevisionIds.push(revision.revid); } }); }); } return unique(visibleRevisionIds); } //Find revisions by the target user belonging to pages which have been deleted. RevisionDelete type=archive requires a target page title and uses the archived revision timestamp as the ID. async function getArchivedRevisionsByTitle() { const targetUsername = mw.config.get('wgRelevantUserName'); let pages = await queryAll({ list: 'alldeletedrevisions', adruser: targetUsername, adrprop: 'ids|timestamp|user', adrlimit: 'max' },function(data) { return (data.query && data.query.alldeletedrevisions) || []; } ); let byTitle = new Map(); pages.forEach(function(page) { let timestamps = byTitle.get(page.title) || []; (page.revisions || []).forEach(function(revision) { if (revision.timestamp && !hasOwn(revision,'userhidden')) { timestamps.push(revision.timestamp); } }); if (timestamps.length) { byTitle.set(page.title,unique(timestamps)); } }); return byTitle; } //Find visible log entries performed by targetUsername. Keep the complete log-event object rather than immediately reducing the result to log IDs because we need row.type later to identify "newusers". async function getVisibleLogEntries() { const targetUsername = mw.config.get('wgRelevantUserName'); let rows = await queryAll( { list: 'logevents', leuser: targetUsername, leprop: 'ids|user|type|timestamp', lelimit: 'max' },function(data) { return (data.query && data.query.logevents) || []; } ); let byId = new Map(); rows.forEach(function(row) { if (row.logid && !hasOwn(row,'userhidden')) { byId.set(row.logid,row); } }); return Array.from(byId.values()); } //Perform a RevisionDelete request. async function revisionDelete(type, ids, reason, target, hide) { const operatorUsername = mw.config.get('wgUserName'); let api = new mw.Api(); let params = { action: 'revisiondelete', type: type, ids: ids.join('|'), hide: hide || 'user', suppress: 'nochange', reason: reason, assert: 'user', assertuser: operatorUsername, maxlag: 5, format: 'json', formatversion: 2 }; if (target) { params.target = target; } return api.postWithToken('csrf',params); } //Process ordinary log IDs in batches. async function hideIdList(type, ids, reason, target, hide, description) { const BATCH_SIZE = 50; let batches = chunks(ids,BATCH_SIZE); for (let i = 0; i < batches.length; i++) { displayStatus(null,(description || type) + ': batch ' + (i + 1) + ' of ' + batches.length + '.'); await revisionDelete(type,batches[i],reason,target,hide); } } //Live revisions must be processed separately for each page title. async function hideLiveRevisions(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let ids = entries[i][1]; let batches = chunks(ids,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Live edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('revision',batches[j],reason,title,'user'); } } } //Archived revisions must be processed separately for each deleted page title. For type=archive, the IDs are archive timestamps. async function hideArchived(byTitle, reason) { const BATCH_SIZE = 50; let entries = Array.from(byTitle.entries()); for (let i = 0; i < entries.length; i++) { let title = entries[i][0]; let timestamps = entries[i][1]; let batches = chunks(timestamps,BATCH_SIZE); for (let j = 0; j < batches.length; j++) { displayStatus(null,'Deleted edits: page ' + (i + 1) + ' of ' + entries.length + ', batch ' + (j + 1) + ' of ' + batches.length + '.'); await revisionDelete('archive',batches[j],reason,title,'user'); } } } //</nowiki> 6y4h01a9gbesg3q34vp7pj0mtbwe5dn 760386 760379 2026-09-05T02:55:26Z Oshwah 26741 Blank code. It has been migrated to RevDelUsername.js. 760386 javascript text/javascript //<nowiki> //CODE GOES HERE! //</nowiki> 6kouw5i7l4c1krqn6lkttry4sdoxbzn WET Awards 2015 0 178214 760366 2026-09-05T00:34:33Z ~2026-47760-51 75527 Created page with "{{Short description|American entertainment awards ceremony}} {{Use mdy dates|date=February 2025}} {{Infobox award | name = 15th WET Awards | image = | imagesize= | caption = | date = June 28, 2015 | location = [[Microsoft Theater]]<br>[[Los Angeles]], [[California]] | presenter = [[WET (TV channel)|White Entertainment Television]] | host = [[Jack Black]]<br/>[[Sarah Jessica Parker]] | network = [[WET (TV channel)|WET]] | producer = | previous = WET..." 760366 wikitext text/x-wiki {{Short description|American entertainment awards ceremony}} {{Use mdy dates|date=February 2025}} {{Infobox award | name = 15th WET Awards | image = | imagesize= | caption = | date = June 28, 2015 | location = [[Microsoft Theater]]<br>[[Los Angeles]], [[California]] | presenter = [[WET (TV channel)|White Entertainment Television]] | host = [[Jack Black]]<br/>[[Sarah Jessica Parker]] | network = [[WET (TV channel)|WET]] | producer = | previous = [[WET Awards 2014|14th]] | main = {{nowrap|[[WET Awards]]}} | next = [[WET Awards 2016|16th]] }} The '''15th [[WET Awards]]''' was held at the [[Microsoft Theater]] in [[Los Angeles]], [[California]] on June 28, 2015 and was televised on [[WET (TV channel)|WET]]. This year marked the ceremony's 15th anniversary and the milestone was celebrated throughout the show, which was hosted by [[Jack Black]] and [[Sarah Jessica Parker]]. ==Nominations and winners== ==Performers== ==Presenters== ==Special awards== ==References== {{reflist|30em}} ==External links== {{WET Awards}} {{DEFAULTSORT:Wet Awards Of 2015}} [[Category:WET Awards]] [[Category:2015 music awards]] [[Category:2015 awards in the United States]] 04s8wvrgddq0b1qyzbowqtbnhn4wjx0 User:Oshwah/MassRevDel.js 2 178215 760380 2026-09-05T02:35:32Z Oshwah 26741 Copy script code to testwiki. 760380 javascript text/javascript //Copied from [[en:User:Writ_Keeper/Scripts/massRevdel.js]] $(document).ready( function() { mw.loader.using(["mediawiki.api"]).then(function () { var apiLimit = 500; var api = new mw.Api(); if(mw.config.get("wgCanonicalSpecialPageName") == "Contributions") { wkContribsCheckboxInit = true; $("ul.mw-contributions-list:first").before("<div style='display:inline-block;' id='revdelCP'><span id='revdelLabel' style='cursor:pointer;'>Revision deletion\ <img id='revdelLabelImg' src='/w/load.php?modules=oojs-ui.styles.icons-movement&image=expand&format=rasterized&lang=en' style='width:12px; transform:rotate(270deg);'/></span>\ <span id='revdelCPBody' style='display:none;'><input type='button' id='revdelSelectAll' value='Select all'><input type='button' id='revdelSelectNone' value='Select none'>\ <input type='button' id='revdelSelectInv' value='Invert selection'>\ <span style='white-space:nowrap;'><input type='checkbox' id='revdelContent' name='revdelOptions' value='content'> delete content</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='revdelName' name='revdelOptions' value='userName'> delete user name</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='revdelComment' name='revdelOptions' value='editSummary'> delete edit summary</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='undelContent' name='revdelOptions' value='content'> undelete content</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='undelName' name='revdelOptions' value='userName'> undelete user name</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='undelComment' name='revdelOptions' value='editSummary'> undelete edit summary</span> \ <br/><select id='wpRevDeleteReasonList'><option value='other'>Other reason</option></select><input name='wpReason' size='60' id='wpReason' maxlength='100'>\ <input type='button' class='revdelSubmit' id='revdelSubmit' value='Revdel selected entries'> \ <input type='button' class='revdelSubmit' id='oversightSubmit' value='Oversight selected entries'></span></div>"); $("ul.mw-contributions-list .mw-revdelundel-link").each(function(ind,el){ if($(this).children("a").length > 0) { var revId = /ids=(\d+)/.exec($(this).children("a").attr("href"))[1]; var pageTitle = /target=([^&]+)/.exec($(this).children("a").attr("href"))[1]; el.innerHTML = "<input type='checkbox' name='"+decodeURIComponent(pageTitle)+"' class='revdelIds' value='"+revId+"'>"; $(el).children(".revdelIds").data("index", ind); } }); $("ul.mw-contributions-list").after("</form>"); $("#revdelCP").data("revdelToolbarShowing", false); $("#revdelCP").data("lastSelectedIndex", -1); //load canned summaries $.get("/w/index.php?title=MediaWiki:Revdelete-reason-dropdown&action=raw",function(data) { reasons = data.replace(/\*\* ([^\*]+)/g, '<option value="$1">$1</option>'); reasons = reasons.replace(/\* ([^<]+)([^\*]+)/g, '<optgroup label="$1">$2</optgroup>'); $('#wpRevDeleteReasonList').append(reasons); }).done(function() { $.get("/w/index.php?title=MediaWiki:Revdelete-reason-dropdown-suppress&action=raw", function(data) { reasons = data.replace(/\*\* ([^\*]+)/g, '<option value="$1">$1</option>'); reasons = reasons.replace(/\* ([^<]+)([^\*]+)/g, '<optgroup label="$1">$2</optgroup>'); $('#wpRevDeleteReasonList').append(reasons); }); }); //attach handlers $("#revdelLabel").click( function() { if($("#revdelCP").data("revdelToolbarShowing")) { $("#revdelCP").data("revdelToolbarShowing", false); $("#revdelLabelImg").css("transform","rotate(270deg)"); $("#revdelCPBody").fadeOut(); } else { $("#revdelCP").data("revdelToolbarShowing", true); $("#revdelLabelImg").css("transform","none"); $("#revdelCPBody").fadeIn(); } } ); $("#revdelSelectAll").click( function() { $('input.revdelIds').each(function() { $(this).prop("checked", true); }); $("#revdelCP").data("lastSelectedIndex", -1); } ); $("#revdelSelectNone").click( function() { $('input.revdelIds').each(function() { $(this).prop("checked", false); }); $("#revdelCP").data("lastSelectedIndex", -1); } ); $("#revdelSelectInv").click( function() { $('input.revdelIds').each(function() { $(this).prop("checked", !($(this).prop("checked"))); }); $("#revdelCP").data("lastSelectedIndex", -1); } ); $("input.revdelIds").off("click").click( function(ev) { var lastSelectedRevdel = $("#revdelCP").data("lastSelectedIndex"); var newIndex = $(this).data("index") if(ev.shiftKey && lastSelectedRevdel >= 0) { var checkboxArray = $("input.revdelIds"); var start = lastSelectedRevdel; var stop = newIndex; if(start < stop) { for(var i = start; i < stop; i++) { if(i != lastSelectedRevdel) { $(checkboxArray[i]).prop("checked", !($(checkboxArray[i]).prop("checked"))); } } } else { for(var i = start; i > stop; i--) { if(i != lastSelectedRevdel) { $(checkboxArray[i]).prop("checked", !($(checkboxArray[i]).prop("checked"))); } } } } $("#revdelCP").data("lastSelectedIndex", newIndex); } ); $("#revdelSubmit").click( function() { //figure out which revisions and pages we're working on. var revCount = 0; var numTitles = 0; var pageTitles = {}; $("input.revdelIds:checked").each(function(ind) { revCount = ind + 1; if(ind > apiLimit - 1) { mw.notify("You can't do more than " + apiLimit + " revdels at once! Canceling..."); return false; } if(typeof pageTitles[$(this).attr("name")] == "undefined") { pageTitles[$(this).attr("name")] = $(this).val(); numTitles++; } else { pageTitles[$(this).attr("name")] = pageTitles[$(this).attr("name")] + "|" + $(this).val(); } }); if(numTitles == 0) { mw.notify("You didn't select any revisions to delete!"); return false; } var confirmString = "You are attempting to modify " + revCount + " revisions.\n\nThe following revision attributes will be changed:\n"; //figure out what we're doing to each revision. This is pretty clunky, but whatever. var deleteString = ""; var revealString = ""; var typeString = "revdel"; if($("#revdelContent").prop("checked") == $("#undelContent").prop("checked")) { confirmString = confirmString + "-Content visibility won't change.\n"; } else if($("#revdelContent").prop("checked")) { deleteString = "content"; confirmString = confirmString + "-Content will be deleted.\n"; } else { revealString = "content"; confirmString = confirmString + "-Content will be revealed.\n"; } if($("#revdelComment").prop("checked") == $("#undelComment").prop("checked")) { confirmString = confirmString + "-Edit summary visibility won't change.\n"; } else if($("#revdelComment").prop("checked")) { if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "comment"; confirmString = confirmString + "-Edit summary will be deleted.\n"; } else { if(revealString != "") { revealString = revealString + "|"; } revealString = revealString + "comment"; confirmString = confirmString + "-Edit summary will be revealed.\n"; } if($("#revdelName").prop("checked") == $("#undelName").prop("checked")) { confirmString = confirmString + "-User name visibility won't change.\n"; } else if($("#revdelName").prop("checked")) { if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "user"; confirmString = confirmString + "-User name will be deleted.\n"; } else { if(revealString != "") { revealString = revealString + "|"; } revealString = revealString + "user"; confirmString = confirmString + "-User name will be revealed.\n"; } if(deleteString == "" && revealString == "") { mw.notify("You didn't select any properties of the revisions to change!"); return false; } var summary = ""; //construct the revdel summary if($("#wpRevDeleteReasonList").val() == "other") { if($("#wpReason").val() == "") { mw.notify("You didn't select or write in an edit summary for the logs!"); return false; } summary = $("#wpReason").val(); } else { summary = $("#wpRevDeleteReasonList").val(); if($("#wpReason").val() != "") { summary = summary + ": " + $("#wpReason").val(); } } confirmString = confirmString + "\nYour revdel summary is: "+ summary +"\n\nAre you sure you want to do this?"; if(confirm(confirmString)) { var numCompleted = 0; for (title in pageTitles) { var ajaxData; ajaxData = {action:"revisiondelete",type:"revision",ids:pageTitles[title],reason:summary}; if(deleteString != "") { ajaxData.hide=deleteString; } if(revealString != "") { ajaxData.show=revealString; } api.postWithToken('csrf', ajaxData).done(function() { numCompleted++; if(numCompleted == numTitles) { mw.notify("modified " + revCount + " revision(s) successfully!"); return false; } }); } } return false; } ) $("#oversightSubmit").click( function() { //figure out which revisions and pages we're working on. var revCount = 0; var numTitles = 0; var pageTitles = {}; $("input.revdelIds:checked").each(function(ind) { revCount = ind + 1; if(ind > apiLimit - 1) { mw.notify("You can't do more than " + apiLimit + " revdels at once! Canceling..."); return false; } if(typeof pageTitles[$(this).attr("name")] == "undefined") { pageTitles[$(this).attr("name")] = $(this).val(); numTitles++; } else { pageTitles[$(this).attr("name")] = pageTitles[$(this).attr("name")] + "|" + $(this).val(); } }); if(numTitles == 0) { mw.notify("You didn't select any revisions to delete!"); return false; } var confirmString = "You are attempting to modify " + revCount + " revisions.\n\nThe following revision attributes will be changed:\n"; //figure out what we're doing to each revision. This is pretty clunky, but whatever. var deleteString = ""; var oversighting = -1; if($("#revdelContent").prop("checked") == $("#undelContent").prop("checked")) { confirmString = confirmString + "-Content visibility won't change.\n"; } else if($("#revdelContent").prop("checked")) { deleteString = "content"; confirmString = confirmString + "-Content will be oversighted.\n"; oversighting = 1; } else { if(oversighting == 1) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } deleteString = "content"; confirmString = confirmString + "-Content will be returned to normal (admin-only) revision deletion.\n"; oversighting = 0; } if($("#revdelComment").prop("checked") == $("#undelComment").prop("checked")) { confirmString = confirmString + "-Edit summary visibility won't change.\n"; } else if($("#revdelComment").prop("checked")) { if(oversighting == 0) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "comment"; confirmString = confirmString + "-Edit summary will be oversighted.\n"; oversighting = 1; } else { if(oversighting == 1) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "comment"; confirmString = confirmString + "-Edit summary will be returned to normal (admin-only) revision deletion.\n"; oversighting = 0; } if($("#revdelName").prop("checked") == $("#undelName").prop("checked")) { confirmString = confirmString + "-User name visibility won't change.\n"; } else if($("#revdelName").prop("checked")) { if(oversighting == 0) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "user"; confirmString = confirmString + "-User name will be oversighted.\n"; oversighting = 1; } else { if(oversighting == 1) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "user"; confirmString = confirmString + "-User name will be returned to normal (admin-only) revision deletion.\n"; oversighting = 0; } if(deleteString == "") { mw.notify("You didn't select any properties of the revisions to change!"); return false; } var summary = ""; //construct the revdel summary if($("#wpRevDeleteReasonList").val() == "other") { if($("#wpReason").val() == "") { mw.notify("You didn't select or write in an edit summary for the logs!"); return false; } summary = $("#wpReason").val(); } else { summary = $("#wpRevDeleteReasonList").val(); if($("#wpReason").val() != "") { summary = summary + ": " + $("#wpReason").val(); } } confirmString = confirmString + "\nYour oversight summary is: "+ summary +"\n\nAre you sure you want to do this?"; if(confirm(confirmString)) { var numCompleted = 0; for (title in pageTitles) { var ajaxData; ajaxData = {action:"revisiondelete",type:"revision",ids:pageTitles[title],reason:summary,hide:deleteString}; if(oversighting == 1) { ajaxData.suppress = "yes"; } else if(oversighting == 0) { ajaxData.suppress = "no"; } else { mw.notify("Something went wrong, so we're going to abort without doing anything."); } api.postWithToken('csrf', ajaxData).done(function() { numCompleted++; if(numCompleted == numTitles) { mw.notify("modified " + revCount + " revision(s) successfully!"); return false; } }); } } return false; } ) } }); }); nybg8rh34fgz5a62newblv5ey50un9t 760382 760380 2026-09-05T02:37:53Z Oshwah 26741 Add deleted contribs. 760382 javascript text/javascript //Copied from [[en:User:Writ_Keeper/Scripts/massRevdel.js]] $(document).ready( function() { mw.loader.using(["mediawiki.api"]).then(function () { var apiLimit = 500; var api = new mw.Api(); const specialPageName = mw.config.get("wgCanonicalSpecialPageName"); if(specialPageName == "Contributions" || specialPageName == "DeletedContributions") { wkContribsCheckboxInit = true; $("ul.mw-contributions-list:first").before("<div style='display:inline-block;' id='revdelCP'><span id='revdelLabel' style='cursor:pointer;'>Revision deletion\ <img id='revdelLabelImg' src='/w/load.php?modules=oojs-ui.styles.icons-movement&image=expand&format=rasterized&lang=en' style='width:12px; transform:rotate(270deg);'/></span>\ <span id='revdelCPBody' style='display:none;'><input type='button' id='revdelSelectAll' value='Select all'><input type='button' id='revdelSelectNone' value='Select none'>\ <input type='button' id='revdelSelectInv' value='Invert selection'>\ <span style='white-space:nowrap;'><input type='checkbox' id='revdelContent' name='revdelOptions' value='content'> delete content</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='revdelName' name='revdelOptions' value='userName'> delete user name</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='revdelComment' name='revdelOptions' value='editSummary'> delete edit summary</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='undelContent' name='revdelOptions' value='content'> undelete content</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='undelName' name='revdelOptions' value='userName'> undelete user name</span> \ <span style='white-space:nowrap;'><input type='checkbox' id='undelComment' name='revdelOptions' value='editSummary'> undelete edit summary</span> \ <br/><select id='wpRevDeleteReasonList'><option value='other'>Other reason</option></select><input name='wpReason' size='60' id='wpReason' maxlength='100'>\ <input type='button' class='revdelSubmit' id='revdelSubmit' value='Revdel selected entries'> \ <input type='button' class='revdelSubmit' id='oversightSubmit' value='Oversight selected entries'></span></div>"); $("ul.mw-contributions-list .mw-revdelundel-link").each(function(ind,el){ if($(this).children("a").length > 0) { var revId = /ids=(\d+)/.exec($(this).children("a").attr("href"))[1]; var pageTitle = /target=([^&]+)/.exec($(this).children("a").attr("href"))[1]; el.innerHTML = "<input type='checkbox' name='"+decodeURIComponent(pageTitle)+"' class='revdelIds' value='"+revId+"'>"; $(el).children(".revdelIds").data("index", ind); } }); $("ul.mw-contributions-list").after("</form>"); $("#revdelCP").data("revdelToolbarShowing", false); $("#revdelCP").data("lastSelectedIndex", -1); //load canned summaries $.get("/w/index.php?title=MediaWiki:Revdelete-reason-dropdown&action=raw",function(data) { reasons = data.replace(/\*\* ([^\*]+)/g, '<option value="$1">$1</option>'); reasons = reasons.replace(/\* ([^<]+)([^\*]+)/g, '<optgroup label="$1">$2</optgroup>'); $('#wpRevDeleteReasonList').append(reasons); }).done(function() { $.get("/w/index.php?title=MediaWiki:Revdelete-reason-dropdown-suppress&action=raw", function(data) { reasons = data.replace(/\*\* ([^\*]+)/g, '<option value="$1">$1</option>'); reasons = reasons.replace(/\* ([^<]+)([^\*]+)/g, '<optgroup label="$1">$2</optgroup>'); $('#wpRevDeleteReasonList').append(reasons); }); }); //attach handlers $("#revdelLabel").click( function() { if($("#revdelCP").data("revdelToolbarShowing")) { $("#revdelCP").data("revdelToolbarShowing", false); $("#revdelLabelImg").css("transform","rotate(270deg)"); $("#revdelCPBody").fadeOut(); } else { $("#revdelCP").data("revdelToolbarShowing", true); $("#revdelLabelImg").css("transform","none"); $("#revdelCPBody").fadeIn(); } } ); $("#revdelSelectAll").click( function() { $('input.revdelIds').each(function() { $(this).prop("checked", true); }); $("#revdelCP").data("lastSelectedIndex", -1); } ); $("#revdelSelectNone").click( function() { $('input.revdelIds').each(function() { $(this).prop("checked", false); }); $("#revdelCP").data("lastSelectedIndex", -1); } ); $("#revdelSelectInv").click( function() { $('input.revdelIds').each(function() { $(this).prop("checked", !($(this).prop("checked"))); }); $("#revdelCP").data("lastSelectedIndex", -1); } ); $("input.revdelIds").off("click").click( function(ev) { var lastSelectedRevdel = $("#revdelCP").data("lastSelectedIndex"); var newIndex = $(this).data("index") if(ev.shiftKey && lastSelectedRevdel >= 0) { var checkboxArray = $("input.revdelIds"); var start = lastSelectedRevdel; var stop = newIndex; if(start < stop) { for(var i = start; i < stop; i++) { if(i != lastSelectedRevdel) { $(checkboxArray[i]).prop("checked", !($(checkboxArray[i]).prop("checked"))); } } } else { for(var i = start; i > stop; i--) { if(i != lastSelectedRevdel) { $(checkboxArray[i]).prop("checked", !($(checkboxArray[i]).prop("checked"))); } } } } $("#revdelCP").data("lastSelectedIndex", newIndex); } ); $("#revdelSubmit").click( function() { //figure out which revisions and pages we're working on. var revCount = 0; var numTitles = 0; var pageTitles = {}; $("input.revdelIds:checked").each(function(ind) { revCount = ind + 1; if(ind > apiLimit - 1) { mw.notify("You can't do more than " + apiLimit + " revdels at once! Canceling..."); return false; } if(typeof pageTitles[$(this).attr("name")] == "undefined") { pageTitles[$(this).attr("name")] = $(this).val(); numTitles++; } else { pageTitles[$(this).attr("name")] = pageTitles[$(this).attr("name")] + "|" + $(this).val(); } }); if(numTitles == 0) { mw.notify("You didn't select any revisions to delete!"); return false; } var confirmString = "You are attempting to modify " + revCount + " revisions.\n\nThe following revision attributes will be changed:\n"; //figure out what we're doing to each revision. This is pretty clunky, but whatever. var deleteString = ""; var revealString = ""; var typeString = "revdel"; if($("#revdelContent").prop("checked") == $("#undelContent").prop("checked")) { confirmString = confirmString + "-Content visibility won't change.\n"; } else if($("#revdelContent").prop("checked")) { deleteString = "content"; confirmString = confirmString + "-Content will be deleted.\n"; } else { revealString = "content"; confirmString = confirmString + "-Content will be revealed.\n"; } if($("#revdelComment").prop("checked") == $("#undelComment").prop("checked")) { confirmString = confirmString + "-Edit summary visibility won't change.\n"; } else if($("#revdelComment").prop("checked")) { if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "comment"; confirmString = confirmString + "-Edit summary will be deleted.\n"; } else { if(revealString != "") { revealString = revealString + "|"; } revealString = revealString + "comment"; confirmString = confirmString + "-Edit summary will be revealed.\n"; } if($("#revdelName").prop("checked") == $("#undelName").prop("checked")) { confirmString = confirmString + "-User name visibility won't change.\n"; } else if($("#revdelName").prop("checked")) { if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "user"; confirmString = confirmString + "-User name will be deleted.\n"; } else { if(revealString != "") { revealString = revealString + "|"; } revealString = revealString + "user"; confirmString = confirmString + "-User name will be revealed.\n"; } if(deleteString == "" && revealString == "") { mw.notify("You didn't select any properties of the revisions to change!"); return false; } var summary = ""; //construct the revdel summary if($("#wpRevDeleteReasonList").val() == "other") { if($("#wpReason").val() == "") { mw.notify("You didn't select or write in an edit summary for the logs!"); return false; } summary = $("#wpReason").val(); } else { summary = $("#wpRevDeleteReasonList").val(); if($("#wpReason").val() != "") { summary = summary + ": " + $("#wpReason").val(); } } confirmString = confirmString + "\nYour revdel summary is: "+ summary +"\n\nAre you sure you want to do this?"; if(confirm(confirmString)) { var numCompleted = 0; for (title in pageTitles) { var ajaxData; ajaxData = {action:"revisiondelete",type:"revision",ids:pageTitles[title],reason:summary}; if(deleteString != "") { ajaxData.hide=deleteString; } if(revealString != "") { ajaxData.show=revealString; } api.postWithToken('csrf', ajaxData).done(function() { numCompleted++; if(numCompleted == numTitles) { mw.notify("modified " + revCount + " revision(s) successfully!"); return false; } }); } } return false; } ) $("#oversightSubmit").click( function() { //figure out which revisions and pages we're working on. var revCount = 0; var numTitles = 0; var pageTitles = {}; $("input.revdelIds:checked").each(function(ind) { revCount = ind + 1; if(ind > apiLimit - 1) { mw.notify("You can't do more than " + apiLimit + " revdels at once! Canceling..."); return false; } if(typeof pageTitles[$(this).attr("name")] == "undefined") { pageTitles[$(this).attr("name")] = $(this).val(); numTitles++; } else { pageTitles[$(this).attr("name")] = pageTitles[$(this).attr("name")] + "|" + $(this).val(); } }); if(numTitles == 0) { mw.notify("You didn't select any revisions to delete!"); return false; } var confirmString = "You are attempting to modify " + revCount + " revisions.\n\nThe following revision attributes will be changed:\n"; //figure out what we're doing to each revision. This is pretty clunky, but whatever. var deleteString = ""; var oversighting = -1; if($("#revdelContent").prop("checked") == $("#undelContent").prop("checked")) { confirmString = confirmString + "-Content visibility won't change.\n"; } else if($("#revdelContent").prop("checked")) { deleteString = "content"; confirmString = confirmString + "-Content will be oversighted.\n"; oversighting = 1; } else { if(oversighting == 1) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } deleteString = "content"; confirmString = confirmString + "-Content will be returned to normal (admin-only) revision deletion.\n"; oversighting = 0; } if($("#revdelComment").prop("checked") == $("#undelComment").prop("checked")) { confirmString = confirmString + "-Edit summary visibility won't change.\n"; } else if($("#revdelComment").prop("checked")) { if(oversighting == 0) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "comment"; confirmString = confirmString + "-Edit summary will be oversighted.\n"; oversighting = 1; } else { if(oversighting == 1) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "comment"; confirmString = confirmString + "-Edit summary will be returned to normal (admin-only) revision deletion.\n"; oversighting = 0; } if($("#revdelName").prop("checked") == $("#undelName").prop("checked")) { confirmString = confirmString + "-User name visibility won't change.\n"; } else if($("#revdelName").prop("checked")) { if(oversighting == 0) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "user"; confirmString = confirmString + "-User name will be oversighted.\n"; oversighting = 1; } else { if(oversighting == 1) { mw.notify("we can't oversight and un-oversight edits at the same time!"); return false; } if(deleteString != "") { deleteString = deleteString + "|"; } deleteString = deleteString + "user"; confirmString = confirmString + "-User name will be returned to normal (admin-only) revision deletion.\n"; oversighting = 0; } if(deleteString == "") { mw.notify("You didn't select any properties of the revisions to change!"); return false; } var summary = ""; //construct the revdel summary if($("#wpRevDeleteReasonList").val() == "other") { if($("#wpReason").val() == "") { mw.notify("You didn't select or write in an edit summary for the logs!"); return false; } summary = $("#wpReason").val(); } else { summary = $("#wpRevDeleteReasonList").val(); if($("#wpReason").val() != "") { summary = summary + ": " + $("#wpReason").val(); } } confirmString = confirmString + "\nYour oversight summary is: "+ summary +"\n\nAre you sure you want to do this?"; if(confirm(confirmString)) { var numCompleted = 0; for (title in pageTitles) { var ajaxData; ajaxData = {action:"revisiondelete",type:"revision",ids:pageTitles[title],reason:summary,hide:deleteString}; if(oversighting == 1) { ajaxData.suppress = "yes"; } else if(oversighting == 0) { ajaxData.suppress = "no"; } else { mw.notify("Something went wrong, so we're going to abort without doing anything."); } api.postWithToken('csrf', ajaxData).done(function() { numCompleted++; if(numCompleted == numTitles) { mw.notify("modified " + revCount + " revision(s) successfully!"); return false; } }); } } return false; } ) } }); }); 9wp05nepjueqfonuqsdbbubhqmaa7md