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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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>]] [<span style="color:#9C59D1">they</span>/<span style="color:#FFC107">them</span>] • [[[User talk:enbi|talk]]]</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; మాథర్, ఏషియన్ పెయింట్స్తో అనుబంధించబడిన ప్రకటనల ఏజెన్సీ, 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]] | [[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]] | [[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]] | [[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]] | [[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]] | [[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}} <!-- [[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>
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}} <!-- [[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>
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}} <!-- [[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>
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}} – $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}} – $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/ العربية] ·
[https://de.wikipedia.org/wiki/ Deutsch] ·
[https://es.wikipedia.org/wiki/ Español] ·
[https://fa.wikipedia.org/wiki/ فارسی]‎ ·
[https://fr.wikipedia.org/wiki/ Français] ·
[https://it.wikipedia.org/wiki/ Italiano] ·
[https://nl.wikipedia.org/wiki/ Nederlands] ·
[https://ja.wikipedia.org/wiki/ 日本語] ·
[https://pl.wikipedia.org/wiki/ Polski] ·
[https://pt.wikipedia.org/wiki/ Português] ·
[https://ru.wikipedia.org/wiki/ Русский] ·
[https://sv.wikipedia.org/wiki/ Svenska] ·
[https://uk.wikipedia.org/wiki/ Українська] ·
[https://vi.wikipedia.org/wiki/ Tiếng Việt] ·
[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] ·
[https://ms.wikipedia.org/wiki/ Bahasa Melayu] ·
[https://nan.wikipedia.org/wiki/ 閩南語] ·
[https://bg.wikipedia.org/wiki/ Български] ·
[https://ca.wikipedia.org/wiki/ Català] ·
[https://cs.wikipedia.org/wiki/ Čeština] ·
[https://da.wikipedia.org/wiki/ Dansk] ·
[https://et.wikipedia.org/wiki/ Eesti] ·
[https://el.wikipedia.org/wiki/ Ελληνικά] ·
[https://eo.wikipedia.org/wiki/ Esperanto] ·
[https://eu.wikipedia.org/wiki/ Euskara] ·
[https://he.wikipedia.org/wiki/ עברית] ·
[https://hy.wikipedia.org/wiki/ Հայերեն] ·
[https://ko.wikipedia.org/wiki/ 한국어] ·
[https://hu.wikipedia.org/wiki/ Magyar] ·
[https://no.wikipedia.org/wiki/ Norsk] ·
[https://ro.wikipedia.org/wiki/ Română] ·
[https://simple.wikipedia.org/wiki/ Simple English] ·
[https://sk.wikipedia.org/wiki/ Slovenčina] ·
[https://sr.wikipedia.org/wiki/ Српски] ·
[https://sh.wikipedia.org/wiki/ Srpskohrvatski] ·
[https://fi.wikipedia.org/wiki/ Suomi] ·
[https://tr.wikipedia.org/wiki/ Türkçe] ·
[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] ·
[https://az.wikipedia.org/wiki/ Azərbaycanca] ·
[https://bn.wikipedia.org/wiki/ বাংলা] ·
[https://bs.wikipedia.org/wiki/ Bosanski] ·
[https://ckb.wikipedia.org/wiki/ کوردی] ·
[https://fy.wikipedia.org/wiki/ Frysk] ·
[https://ga.wikipedia.org/wiki/ Gaeilge] ·
[https://gl.wikipedia.org/wiki/ Galego] ·
[https://hr.wikipedia.org/wiki/ Hrvatski] ·
[https://ka.wikipedia.org/wiki/ ქართული] ·
[https://ku.wikipedia.org/wiki/ Kurdî] ·
[https://lv.wikipedia.org/wiki/ Latviešu] ·
[https://lt.wikipedia.org/wiki/ Lietuvių] ·
[https://ml.wikipedia.org/wiki/ മലയാളം] ·
[https://mk.wikipedia.org/wiki/ Македонски] ·
[https://my.wikipedia.org/wiki/ မြန်မာဘာသာ] ·
[https://nn.wikipedia.org/wiki/ Norsk nynorsk] ·
[https://pa.wikipedia.org/wiki/ ਪੰਜਾਬੀ] ·
[https://sq.wikipedia.org/wiki/ Shqip] ·
[https://sl.wikipedia.org/wiki/ Slovenščina] ·
[https://th.wikipedia.org/wiki/ ไทย] ·
[https://te.wikipedia.org/wiki/ తెలుగు] ·
[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