Page MenuHomePhabricator

Add export selected files feature to Cat-a-lot
Open, Needs TriagePublicFeature

Assigned To
Authored By
Zache
Mar 6 2025, 1:34 PM
Referenced Files
F58910789: cat-a-lot.js
Mar 24 2025, 5:40 PM
F58907862: newchanges.patch
Mar 24 2025, 8:41 AM
F58887467: my-changes.patch
Mar 21 2025, 9:28 AM

Description

The user should be able to export a list of selected files in various formats. Export should be happen in a dialog where the list can be easily copied and pasted.

The export format should be user-selectable:

  • List of selected pages as plaintext
  • List of selected pages as URLs
  • List of selected pages as a wikitext list
  • List of selected pages as a page_id list
  • List of selected files as a wikitext gallery
  • List of selected files as a media items list

Pages and files difference is that pages can be content from any namespace in Wikimedia Commons. Files are pages in File namespace.

Event Timeline

Hello @Zache I will be working on this task

Hello @Zache , I have added a patch file that includes all the work I have done.

In summary:

I created a new folder called ExportTool in the extensions directory.

This folder contains all my changes.

I also added a dedicated folder for unit and integration tests.

Please review the patch file and let me know if you have any feedback or questions.

Hi, thank you! Using extension was unexpected, but nevertheless it is very cool solution and good work.

Could you give step by step instructions howto install and test it ? :) (I have mediawiki installed using Quickstart quide)

Instructions for Integrating and Testing the ExportTool Extension

Hello @Zache

I used an extension for my work to avoid making changes directly to the MediaWiki codebase. Below are the detailed step-by-step instructions to install and test the feature:


    1. Step 1: Apply the Patch File
  1. Download the Patch File:
    • Download the patch file I uploaded in Phabricator to your local machine.
    • Take note of the path where the file is saved (e.g., /Users/your-username/Downloads/my-changes.patch).
  1. Navigate to the MediaWiki Project:
    • Open your terminal and navigate to the root folder of your MediaWiki installation. For example: `bash cd /path/to/your/mediawiki `
  1. Apply the Patch:
    • Run the following command to apply the patch: `bash git apply /path/to/my-changes.patch `
    • What this does:
      • The git apply command applies the changes from the patch file to your working directory.
      • Replace /path/to/my-changes.patch with the actual path where you saved the downloaded patch file (e.g., /Users/your-username/Downloads/my-changes.patch).
    • If the patch fails:
      • Ensure you’re in the correct directory (the root of your MediaWiki installation).
      • If there are conflicts, you may need to resolve them manually. Let me know if this happens, and I can assist.

    1. Step 2: Enable the Extension
  1. Locate LocalSettings.php:
    • In the root folder of your MediaWiki installation, find the LocalSettings.php file. This file contains configuration settings for your MediaWiki instance.
  1. Add the Extension Loading Command:
    • Open LocalSettings.php in a text editor (e.g., VSCode or any code editor).
    • Add the following line to load the ExportTool extension: `php wfLoadExtension('ExportTool'); `
    • What this does:
      • The wfLoadExtension function tells MediaWiki to load the ExportTool extension from the extensions/ExportTool folder.
      • This ensures that your extension is recognized and integrated into the MediaWiki system.
  1. Save and Close the File:
    • Save the changes to LocalSettings.php and close the editor.

    1. Step 3: Run Unit Tests
  1. Run the Unit Tests:
    • Execute the following command to run the unit tests for the ExportTool extension: `bash composer phpunit -- --group ExportTool `
    • What this does:
      • composer phpunit runs the PHPUnit testing framework for MediaWiki.
      • The --group ExportTool flag ensures that only the tests related to the ExportTool extension are executed.
      • This verifies that the extension works as expected and doesn’t introduce any regressions.
  1. Check the Test Results:
    • If all tests pass, you’ll see output like this: ` OK (5 tests, 5 assertions) `
    • If any tests fail, let me know, and I’ll help debug the issue.

    1. Step 4: Test the Feature
  1. Log in to Your MediaWiki Instance:
    • Access your local MediaWiki instance in your browser (e.g., http://localhost:8080).
    • Log in with your admin or test user account.
  1. Navigate to a Page with Files:
    • Go to a page that contains files or pages you want to export (e.g., Special:ListFiles).
  1. Verify the Output:
    • Check that the exported list matches the selected format.
    • Ensure the feature works as expected and integrates seamlessly with the rest of MediaWiki.

Let me know if you have any questions or run into any issues!, so we can debug togetjer


Thank you. I will comment on the general level about using the extension. I will also give a code review comments when I have a better idea about coding the extensions. 

Using the extension as a solution was a unique method for solving the problem. It also presented skills as a developer. Using macOS as a developer platform was not the most straightforward route either. Given that, I was impressed, as I didn't expect that.

However, there is one big blocker for actually using a new extension as a solution. It would be tough (ie. nearly impossible for us) to get new extension installed in the Wikimedia production environment as it would mean that extension would not only be good and stable code, but it would also require that it is maintained. This would also require whoever has done the extension to have a good track record and ability to maintain the code. Another way would be for Wikimedia Foundation tech teams to pick up and do the maintenance. Still, as they are already committed to their tasks, they are unlikely to extend their support to new things beyond their planned tasks.

Cat-a-lot is focused on Javascript code running in a browser sandbox, and users will opt-in to use it. Because of this, it has more relaxed requirements for code quality, active maintenance, etc., than extensions.

Also, from a code development strategy point of view, instead of creating new extensions, a better way to get changes, etc., that will require server-side support would be to do them to MediaWiki core or some extension that is already in use, as they already have the required level of code review, maintenance and support.

Hi @Zache ,

Thank you for your detailed feedback, guidance, and kind words. I truly appreciate the time you took to review my approach and provide such valuable insights.

I understand the challenges you’ve highlighted regarding introducing a new extension, especially the concerns around maintenance and integration into the Wikimedia production environment. Your suggestion to integrate changes into the MediaWiki core or an existing extension makes a lot of sense, and I will follow this approach.

I will create a new branch and get started, I will upload the patch diff as I have done before.

Thanks again for your support and encouragement. Your feedback has been incredibly helpful, and I’m looking forward to continuing this work with your guidance.

Hi @Zache ,
I hope you had a restful weekend .
I have been able to add the feature Export tool in mediawiki core
kindly see attached patch file
Regards.


Added export feature with 6 formats (plaintext, URLs, wikitext, etc.). Tested in user space.

I had made a huge mistake in the previous works i have done .
The set up development url has shed more light
my mistake was working on mediawiki core when i only need to work on cat-a-lot gadget
I have retraced my steps and added the file and url to my mySpecialPage:

I would like to know how to proceed from here if i have finally done it right .
I have tested the feature and it works correctly

@Zache

Thanks for the solution. It opened the export dialog and one could export the selected files. Some comments about refining the implementation.

Export Selected Files -button
Export selected files button could be initialized in init() instead of run() and it could located after the text "N files selected" text so if there is files selected text would be "N files selected ( export )" where the export would be a link which would open the export dialog.

Export text should be also translatable text similarly than other texts in the app.

Export dialog

  • Dialog could be wider (for example 80% of screen width)
  • Format selection should be done using clickable links as UI is generally clickable instead of numberical text input

Export formats

  • in plaintext format space could be converted to underscores:

(20210528) Leipzig University 01.jpg
to
(20210528)_Leipzig_University_01.jpg

  • in urls:

/w/index.php?title=(20210528)_Leipzig_University_01.jpg&full=true
to
https://commons.wikimedia.org/wiki/File:(20210528)_Leipzig_University_01.jpg"

  • in wikitext list:

[[:(20210528) Leipzig University 01.jpg]]
to
[[:file:(20210528) Leipzig University 01.jpg]]

  • page_ids:

(20210528)_Leipzig_University_01.jpg, (20210528)_Leipzig_University_02.jpg, (20210528)_Leipzig_University_03.jpg ...
to
146546964, 146546962, 146546963 ...

  • mediaitem list:

(20210528)_Leipzig_University_01.jpg, (20210528)_Leipzig_University_02.jpg, (20210528)_Leipzig_University_03.jpg ...
to
M146546964, M146546962, M146546963 ...

Gallery syntax

<gallery>
(20210528)_Leipzig_University_01.jpg,
(20210528)_Leipzig_University_02.jpg
(20210528)_Leipzig_University_03.jpg
</gallery>

getMarkedLabels()
Did you modify this function purpose? If so what you did try to do? Just askin as function is used by external ACDC gadget so i try to figure out if the change is something which will impact to it.

Hi @Zache,

Thank you for your feedback and corrections they were incredibly helpful. I've carefully reviewed your suggestions and implemented the changes accordingly.

You can find the updated implementation here:
JoyAruku Edit.js

Regarding the export functionality:

Initially, I had planned to simplify getMarkedLabels() to:

Streamline title extraction

Ensure clean output without namespace prefixes

Return a more straightforward array structure

However, after restructuring the code, I ultimately:

Added a dedicated getExportItems() function to handle title cleaning and validation

Implemented generateExportData() to properly format the output for each export type

This approach better separates concerns while ensuring the export formats (plaintext, gallery syntax, etc.) work exactly as expected.

Please let me know if you'd like me to clarify any part of the implementation.

Hi, some new notes.

Export text
Currently there is two times the export link, one inside parenthisis (correct one) and second below the selected files info. On one below selected files info works.

This is because you have already created the link once in init(), and in updateSelectionCounter() you're essentially cloning the exportLink when concatenating it using outerHTML. ie. this

countText += ' (' + this.$exportLink.show().get(0).outerHTML + ')';

I think that the correct solution would be to remove the creation from init() and instead create the entire link in updateSelectionCounter().

Opening the dialog

The dialog opens correctly only once. The first time, it displays the selected text properly, but if opened a second time, it appears empty. Additionally, the buttons stop working. This issue was observed in Safari and i didint test it with other browsers. I managed to fix it by fully destroying the dialog from the DOM, but I'm unsure why this issue occurred in the first place. It might be worth asking in the Slack channel to see if anyone knows what's causing this, as there might be a better solution.

Below is my working dialog code

$exportDialog.dialog({
   title: msg('export'),
   width: '80%',
   modal: true,
   buttons: {
       'Copy': function() {
           var $textarea = $('#cat_a_lot_export_data');
           $textarea.select();
           try {
               document.execCommand('copy');
           } catch (e) {
              alert('Copy failed: ' + e.message);
          }
      },
      'Close': function() {
          $(this).dialog('close');
     }
   },
   open: function() {
       // Initialize with first format
       CAL.generateExportData(titles, 'plaintext');
   },
   close: function() {
       $(this).dialog('destroy').remove();
   }
});

Output formats
If we want working filenames, we cannot strip characters out of them. The current filename normalization also affects the retrieval of pageIds and mediaItems via the API, because removing characters means the titles no longer match, causing missing values. A better approach would be to use the titles as-is (without normalization) and request canonical filenames, URLs, and page_ids directly from the API and use them.

You can also query multiple titles at once using | as separator in titles parameter.

Example:

    const api = new mw.Api();
   // Return a promise that resolves with page information
    return api.get({
        action: 'query',
        format: 'json',
        prop: 'info',
        inprop: 'url',
        titles: "Turku|Tampere|Helsinki"
    }).then(function(data) {
        // Navigate through the nested API response
        const pages = data.query.pages;
        const pageId = Object.keys(pages)[0]; // First (and only) key
        const pageInfo = pages[pageId];

        return {
            pageId: pageId,
            fullUrl: pageInfo.fullurl,
            canonicalUrl: pageInfo.canonicalurl,
            title: pageInfo.title
        };
    }).catch(function(error) {
        console.error('Error fetching page information:', error);
        return null;
    });
}

You test API queries using Special:ApiSandbox: Example query

Output formats
There were also some typos in output. For example urls had "https:////" too many slashes. In gallery there were extra comma , after filename which doesn't work as wikitext parser thinks that it is part of the filename. (note. comma was copypaste error in my output example too)

Hi @Zache Thanks a lot for your feedback and for your guidiance too.

Changes Made

I have made changes to effectively resolve the key issues in the export functionality. By removing the duplicate export link creation in init() and consolidating it within updateSelectionCounter(),
I have ensured only one properly working export link appears in the UI.
The dialog now works properly thanks for the line of code you gave me , I tested on chrome browser.
Using the API's native fullurl and batch processing with the pipe separator maintains filename integrity while avoiding URL formatting issues.
The output formats now correctly handle special characters without unwanted commas in gallery syntax.

All changes can be found here joyAruku edit.js

Hi @JoyAruku,

You still try to – and fail to correctly ­– clean up the titles on your own. It’s unnecessary, the API will do all the normalization you need.

getExportItems() can be simplified to:

getExportItems: function () {
	return Array.from( this.getMarkedLabels() ).map( item => item[ 0 ] ); // item is [title, jQueryObject]
},

(and likely inlined due to its simpleness).

The beginning of generateExportData can be changed from

generateExportData: function(titles, format) {
    titles = titles.map(function(title) {
        return title.replace(/^(File|Image|Category|Media):/i, '').trim();
    }).filter(function(title) {
        return title.length > 0;
    });

    const api = new mw.Api();

    api.get({
        action: 'query',
        prop: 'info|imageinfo',
        titles: titles.map(title => `File:${title}`).join('|'),
        inprop: 'url',
        iiprop: 'mediatype',
        formatversion: 2
    }).then(data => {
        const results = data.query.pages;
        let output;

        switch(format) {
            case 'plaintext':
                output = titles.map(title => title.replace(/\s+/g, '_')).join('\n');
                break;

to

generateExportData: function ( titles, format ) {
	const api = new mw.Api();

	api.get( {
		action: 'query',
		prop: 'info|imageinfo',
		titles: titles,
		inprop: 'url',
		iiprop: 'mediatype',
		formatversion: 2
	} ).then( data => {
		const results = data.query.pages;
		let output;

		switch( format ) {
			case 'plaintext':
				output = results.map( page => page.title ).join( '\n' );
				break;

In the latter, the changes are:

  • Don’t try to normalize titles, the API backend will do this.
  • No need to .join('|') the titles, the mw.Api object will do this for you.
  • Use results for plain text output, as titles is no longer normalized.

Furthermore,

  • Optimization: I’d do the API request in exportSelectedFiles(), not in generateExportData() – when the user switches between modes, generateExportData() runs the exact same API request again and again; doing the request in the caller method and using the existing result when switching between modes would reduce the number of HTTP requests.
  • Optimization: There’s no need for .replace(/_/g, ' '), the titles returned by the API already contain spaces, not underscores.
  • Bugs: In gallery mode,
    • non-file pages should be ignored, similarly to the media items list, and
    • you shouldn’t call page.title.replace('File:', ''), as it won’t work on non-English wikis. Instead, please use new mw.Title( page.title ).getMain(), which removes the namespace no matter how it’s called.
  • UX: It’s unclear what the currently selected mode is. I’d use radio buttons rather than links for the mode selection, which would automatically make it visually obvious. (However, if it proves to be too difficult, keeping the links is also acceptable.)
  • Code style: You made a huge whitespace change, which makes the diff much harder to read. Please revert those changes and any other unrelated changes, and follow the whitespace conventions of existing code in new code as well.

Hi @Tacsipacsi ,

Thank you for your thorough review and helpful suggestions. I've implemented the changes you recommended:

  • Simplified getExportItems() as suggested and inlined it
  • Removed manual title normalization and let the API handle it
  • Moved the API call to exportSelectedFiles() to avoid duplicate requests
  • Fixed the gallery mode to properly handle non-file pages using mw.Title
  • Added radio buttons for mode selection with the CSS styling

The updated code is now live in:

joyAruku edit.js

I also added changes to the css file to properly style the radio button which you can find here
joyAruku Edit.css

I believe I've addressed all the points you raised, but please let me know if there's anything else that needs adjustment. I appreciate you taking the time to help improve this feature.

Thanks you for your new version. Some notes about implementation

One problem which arises if you are using api.get() and number of titles is high then url length would be too long in some browsers. If you change it to api.post() then it would work. Also i would chain the creation of the dialog also inside then() so you would know that you have data when the dialog will be created. This would make code little bit cleaner.

There was also a bug in how it loaded data, so that the data loading worked only the first time when the dialog opened. The data was not updated to formats other than plaintext if the export dialog was closed, selections were changed, and the export dialog was reopened.

Hi @Zache

Thank you for your prompt review and feedback. I’ve addressed the issues you highlighted, and everything should now be functioning as expected.

Please don’t hesitate to reach out if you encounter any other bugs or have additional suggestions—I’m happy to make further adjustments.

  • Simplified getExportItems() as suggested and inlined it

Thanks for the simplifying! However, you haven’t actually inlined it – “inlining” means replacing a function/method call with the function/method body, i.e. moving the Array.from(...).map(...) expression to exportSelectedFiles(). Whether or not to inline is partly a matter of optimization, partly a matter of readability (and thus of taste), so I won’t object if you don’t inline it, but I personally think inlining would be beneficial. The method has two callers currently (which would speak against inlining), but the call in generateExportData() shouldn’t exist: you should use the same data source for all formats.

  • Removed manual title normalization and let the API handle it

Thanks, but you don’t actually let the API handle it in case of the plaintext format. As I wrote in the previous paragraph, please use the same data source for all formats.

  • Moved the API call to exportSelectedFiles() to avoid duplicate requests

Unfortunately, this introduced a race condition (which can be seen on the Data not loaded yet error message). To avoid the race condition, please either

  • wait for the data to load before opening the window, as @Zache suggested, or
  • pass the Promise you got from the api.get() (or api.post()) request to generateExportData(), and make the processing (.then() and .catch() calls) there. This latter ensures that the popup appears quickly even if it takes a while to load the data, while the single api.get()/api.post() call still means that the API request is sent only once, and we use the same result every time.

In either case, I wouldn’t use a property, but would pass the list in a method parameter – this makes the data flow easier to understand.

  • Fixed the gallery mode to properly handle non-file pages using mw.Title

Thanks! However, the filtering unfortunately got worse: now you filter out non-file pages in all formats except plain text. You shouldn’t, the only two formats that apply to files only are gallery and media items list, all others (plain text, URLs, wikitext list, page IDs) should show all pages.

And in case of gallery and media items list, you don’t need to check page.ns: page.imageinfo won’t be present on any non-file namespace pages anyway. (So the namespace check is harmless but also useless.)

  • Added radio buttons for mode selection with the CSS styling

Thanks, but it uses a bit too much CSS to my taste. I’d use a <fieldset> and <label>s wrapping the radio buttons: this gives a simple but good-looking result, and the wrapping <label>s make using IDs unnecessary, which have a small potential for clashing with anything else on the page. My proposal:

// Create radio buttons container with title
var $formatContainer = $( '<fieldset>' )
 	.addClass( 'export-radio-group' )
	.append( $( '<legend>' ).text( msg( 'export-formats' ) ) );
var formats = [
	{ id: 'plaintext', name: msg( 'export-plaintext' ) },
	{ id: 'urls', name: msg( 'export-urls' ) },
	{ id: 'wikitext', name: msg( 'export-wikitext' ) },
	{ id: 'pageids', name: msg( 'export-pageids' ) },
	{ id: 'gallery', name: msg( 'export-gallery' ) },
	{ id: 'mediaitems', name: msg( 'export-mediaitems' ) }
];
// Create radio buttons
formats.forEach( format => {
	var $radioContainer = $( '<label>' );
	var $radio = $( '<input>' )
		.attr( {
			type: 'radio',
			name: 'exportFormat',
			value: format.id
		} )
		.prop( 'checked', format.id === 'plaintext' );
	$radioContainer.append(
		$radio,
		document.createTextNode( ' ' + format.name )
	);
	$formatContainer.append($radioContainer);
} );
$formatContainer.on( 'change', 'input[type="radio"]', () => {
	var currentFormat = $( this ).val();
	CAL.generateExportData( currentFormat );
} );

This results in the following HTML (plus moves the variable currentFormat to within the event handler, as the value set there is not read elsewhere, and the value set elsewhere is not read there):

<fieldset class="export-radio-group">
	<legend>Export formats</legend>
	<label><input type="radio" name="exportFormat" value="plaintext"> Plaintext</label>
	<label><input type="radio" name="exportFormat" value="urls"> URLs</label>
	<label><input type="radio" name="exportFormat" value="wikitext"> Wikitext List</label>
	<label><input type="radio" name="exportFormat" value="pageids"> Page IDs</label>
	<label><input type="radio" name="exportFormat" value="gallery"> Wikitext Gallery</label>
	<label><input type="radio" name="exportFormat" value="mediaitems"> Media Items List</label>
</fieldset>
  • Code style: You made a huge whitespace change, which makes the diff much harder to read. Please revert those changes and any other unrelated changes, and follow the whitespace conventions of existing code in new code as well.

This is still a problem.

@Tacsipacsi, thanks for your feedback! Here’s an update on the changes:

  • getExportedItem function: Kept it for reusability but removed it from generateExportData.
  • Plaintext format: Now uses the same data source as other formats.
  • Race condition fix: Moved createExportDialog into promises, calling generateExportData after resolution.
  • Unnecessary filters: Removed redundant filtering logic.
  • Fieldset usage: Implemented <fieldset> for radio buttons as suggested.

Although I couldn't revert to resolve the whitespace issue, the problem occurred when I copied the code from Gadget-Cat-A-Lot and pasted it into my edit page—it was already scattered. However, I removed as much whitespace as possible to make the code more readable.

Regarding inlining getExportItems(), I’ve retained it for readability but ensured consistency in data sourcing. The whitespace/style issues have been corrected.

@JoyAruku : I found couple issues. One was that if there were too many titles selected then it had toomanyvalues error. Second was that when dialog was opened it there was empty text box. However, it was filled succesfully when output type was changed. I made couple of changes to it so i got these working:

Loading data
I modified the loading so that it splits one large query into multiple queries, each containing a maximum of 50 titles. After all queries are done it combines the responses into one large array before calling`this.createExportDialog()` (diff)

Value on creation
I moved setting the initial value to inside this.createExportDialog() as now we know that data is loaded before dialog is opened. (diff)

I think that when those are fixed it works pretty well and it would start to be ready for testing by others. It also starts to be ready for merging. I will leave a separate comment about on how I would clean the code for merging. (ie. removing unrelated changes, some whitespace polishing etc) In any case good work.

@Zache Everything is fixed now, I have tested the changes and it works correctly, I really appreciate your help , patience and mentorship.
Especially your help in fixing the last bug .
I can't even start to express how grateful I am to you .
Thanks once again Zache.

hello @Zache ,
I hope you are having a lovely day, I just wanted to know if my code have been merged .
I have not received any feedback with that regard.
Thanks.

@Tacsipacsi, thanks for your feedback! Here’s an update on the changes:

  • getExportedItem function: Kept it for reusability but removed it from generateExportData.

Thanks!

  • Plaintext format: Now uses the same data source as other formats.

Thanks! However, you don’t need to use mw.Title in this branch (unlike the gallery, where you should): in a plaintext list, we need the prefixed text (e.g. File:Example.svg), which is exactly what the API returns. new mw.Title(...).getMainText(), on the other hand, returns the unprefixed text (e.g. Example.svg), so it’s actively wrong. (Using .getPrefixedText() instead of .getMainText() would solve the issue, but why use it when simply page.title is what we need?)

  • Race condition fix: Moved createExportDialog into promises, calling generateExportData after resolution.

You still use a property (this.exportResults) instead of passing data along in parameters. Even if you now make it race condition-free, it easily regresses. Please use parameters.

  • Unnecessary filters: Removed redundant filtering logic.

You removed too much. :( As I wrote, “the only two formats that apply to files only are gallery and media items list” – that is, you do need to filter in gallery and media items list modes.

  • Fieldset usage: Implemented <fieldset> for radio buttons as suggested.

Thanks! My proposal would have kept the flexbox formatting, but your solution also looks good. (Although I’d add display:inline-block to the <label>s, so that the radio buttons and the texts stay together if the line needs to be wrapped.)

Although I couldn't revert to resolve the whitespace issue, the problem occurred when I copied the code from Gadget-Cat-A-Lot and pasted it into my edit page—it was already scattered. However, I removed as much whitespace as possible to make the code more readable.

Regarding inlining getExportItems(), I’ve retained it for readability but ensured consistency in data sourcing. The whitespace/style issues have been corrected.

I still see a lot of whitespace changes on https://commons.wikimedia.org/wiki/Special:ComparePages?page1=MediaWiki:Gadget-Cat-a-lot.js&page2=User:JoyAruku/common.js. wikEdDiff (which I see you use) reveals the cause: you replaced most tab indentation with four spaces. I don’t know why this happened, so I’m not sure how you can fix it, but here are some ideas:

  • Copy the code in your favorite code editor, use its search and replace feature to replace four spaces with tabs, and copy the code back, hoping that this time you’ll copy tabs.
  • Enable the code editor if you haven’t done so (<> icon on the edit toolbar), press the tab character somewhere, cut it, paste it in the search field of the edit toolbar’s find and replace feature (hoping that it’ll remain a tab), type four spaces in the search field, and click All.
  • Use the same search and replace feature, but type the tab character in that replace field directly. On many (all?) Linuxes, you can do so by pressing ++U then 9 then ; on Windows, it’s Alt+Num0 then Alt+Num9; I’m not familiar with macOS.

Hi @Tacsipacsi, thanks again for the detailed feedback! I’ve made further updates based on your suggestions and wanted to address each point:

  • Plaintext: Fixed to use page.title (prefixed) instead of mw.Title.
  • Race Condition: Removed this.exportResults, now passing data via parameters.
  • Filters: Restored .filter(page => page.imageinfo) for gallery and mediaitems only; others unfiltered.
  • Fieldset: Added display: inline-block to <label>s for wrapping.

Hi @JoyAruku

  • this.generateExportData is not a function error**

Now when dialog is opened it will throw the this.generateExportData is not a function error The error itself can be fixed by changing this back to CAL. (example: diff). The error occurred because inside event handler, the keyword this no longer refers to the CAL (which contains the generateExportData method) but to the DOM element that fired the event.

Code cleanup for merging
The actual export code is nicely encapsulated from main code as it uses the CAL.getMarkedLabels() function for getting the data. This means that it is easy to move export code to separate file ( ie. mediawiki:Gadget-Cat-a-lot.export.js ). Keeping the main file smaller helps with maintainability. Additionally, having the export functionality in a separate file makes merging easier.

I made it so that i created file User:Zache-test/Gadget-Cat-a-lot.export.js. Here is my changes to main code ( diff ) To get the translations working I needed to export msg via CAL ( diff ) and use exported function in Cat-a-lot.export.js. ( diff )

TODO
After moving the main export code to separate file (ie. Gadget-Cat-a-lot.export.js ) I would take latest version of  Mediawiki:Gadget-Cat-a-lot.js as base and I would cherry pick changes to it so that there would be only those changes what you need. With this you get rid of the whitespace differences. Afaik currently there is following changes to Gadget-Cat-a-lot.js

  • Loading of the Gadget-Cat-a-lot.export.js
  • Translations
  • Exporting msg function via CAL.
  • Changes to updateSelectionCounter()

The $exportLink which is created in lines 696 - 706 is not used anywhere as the exportLinkHtml is the visible one so it could be removed.

I also would move the export link CSS styles from javascript to Gadget-Cat-a-lot.css (ie. line https://commons.wikimedia.org/wiki/User:JoyAruku/common.js#L-707 707 )

Hello @Zache ,

Thank you so much for your guidance!

I have resolved the error and cleaned up the code. I’ve also moved the export logic to a separate file, which can be found here: Export file.js.

The code has been cleaned up to remove unnecessary whitespace and now includes all the latest changes from Gadget cat-a-lot.

Thanks again for your mentorship, patience, and guidance!

Hello @Zache Kindly confirm , if the recent charges I made are okay and go to go for merging .
Thanks a lot

Hi @JoyAruku , I made some small changes to Cat-a-lot.js and also fixed the whitespaces so now there is clean diff.

If you want to move the addExportLink() function to https://commons.wikimedia.org/wiki/User:Zache-test/Gadget-cat-a-lot.js#L-520 line 520 from Cat-a-lot.js to User:JoyAruku/export.js call that from line 541 then all relevant code would be on one file.

Only exception is the translations, I left the translations to Gadget-cat-a-lot.js because then all texts which require translations are in one place.

I also moved the export features to the ready for merge list.

Hi @Zache, thanks a lot for your guidance! I've moved the addExport function to my export.js file and cleaned up the code in common.js to match your version.