This time, we’ll add a button to the
#p-cactions dropdown (how to do that was initially introduced in Gadget - Search results new page hotkey) that gives you a button to click to copy all of the members of a category, one per line.
Sometimes we want to copy all of the members of a category. This could be to:
- Ctrl+F through all of the titles in Notepad++
- Create a
pairsfilefor PWB’s movepages script - copy all the category members, then run a regex to generate the destination (something like
$1\r\n$1 - newas the replacement term with
(.*)as the search to move from
pagename - new)
- Provide a list of pages to someone who may be less comfortable with built-in features of tooling, but still wants to run tooling operations on a category (see below)
Things we would (probably) not need this for:
- Working with AutoWikiBrowser - it has a category select as a generator option, and pretty robust filtering options of its own
- Working with PWB if it’s not to move pages -
-categoryname:is a generator
- Writing Python scripts - we’d just use
mwclientto get the listing (
site.client.categoriesif working with mwcleric)
Can’t we just highlight and Ctrl+C?
There’s a couple issues with copying the page list as-is:
- You get the single-letter section titles included as well
- The category could span more than one page, so not all members are included (though this gadget doesn’t actually continue, so it’s limited at
maxmembers which for my purposes is 5000 since I have
apihighlimitson every wiki I work with)
- There’s leading whitespace at the beginning of each line
So it’s pretty annoying to sanitize the input we’d get if we just copied what’s already on the page. (Exercise to the reader - generate a single regex to sanitize the input we get! You’ll have to use at least one pipe.)
It uses this function from Gadget - utils.js:
First, immediately return if we’re not on a category page. Then add a portlet link into
p-cactions with our action; I’ve explained this before.
Next, we do an API call: a simple
query (which is a
get request as opposed to a
postWithToken) of the
categorymembers. We can check the categorymembers documentation to see what parameters are supported, though I almost always just use the API sandbox to check parameters.
We want to specify two parameters:
cmtitle- this is the title of the category we’re querying, and in this case it’s the name of the current page we’re on
cmlimit- set it to max; by default, if we’re an admin, it’ll be 5000, and this should be plenty
Note that this code does NOT support copying the entire category’s members for “large” categories, and if you don’t have the
apihighlimits right, you’ll be gated at only 500 results (by default). If we wanted to change this, we’d have to restructure the code slightly, adding another function called something like
doQuery that accepts a
cmcontinue parameter as well as the current value of our list of known pages. Our
then would then append the new data, and if we have a
query-continue as part of our response from the server (i.e. if we’re not done getting data), would re-call
doQuery with the next value of
cmcontinue set to the
If we were to take this approach, we’d probably want to set our own limit (perhaps 5000) so that (a) we’re never inserting a list of say 50,000 elements to the DOM if a user calls
!Copy members unwittingly on a huge category page and also (b) we’re not making the user wait for dozens of sequential round trips to the server and back before the result is ready.
After we’ve gotten our result, we’ll iterate over the result and push the title of each page into an array, concatenate the result, and then display the output text.
displayOutputText function makes a textarea, sets it to readonly, and then places it right after
#contentSub. It highlights and selects the text inside, but doesn’t execute any copy operation, in case you have sensitive information on your clipboard already.
Why delegate to a helper function instead of just inserting the element immediately? This code is pretty simple.
- Laziness/ease - there’s a few different gadgets that create copyable output like this, and I’d rather not copy-paste the same code multiple times
- Single point of change - if the DOM ever changes so that
#contentSubis no longer the place I’m going to insert the text (hey, that’s happening!) or if I ever want to change other CSS properties about it, there’s a single place to do so
TL;DR: I actually do use it enough that DRY applies.
Like many of my tools, this gadget is a pretty quick and somewhat lazy but still very useful piece of code - effectively I do only 10% of a proper implementation (which would incorporate continuing) but get 90% of the benefit (continuing is almost never needed). Because I make it available only by opt-in, instead of enabling it by default, its roughness is less of a problem, and it’s always open to extension and completion if someone’s needs are ever not being met.
You should always feel free to write incomplete tools and use them! A hacked-together script that does 90% of what you could imagine it capable of doing can be among your most valuable assets! Just don’t mistake incomplete for untested or incorrect; your incomplete tools should still work.