Jump to content

User:AramilFeraxa/FileDelinker.js

From Meta, a Wikimedia project coordination wiki

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
mw.loader.using(['oojs-ui', 'mediawiki.api', 'mediawiki.util']).then(function () {
    $(function () {
        if (mw.config.get('wgNamespaceNumber') !== 6) {
            return;
        }

        function getLocalFileUsage(fileName) {
            return new mw.Api().get({
                action: 'query',
                prop: 'fileusage',
                titles: fileName,
                fulimit: 'max',
                format: 'json'
            }).then(function (data) {
                var pages = [];
                Object.values(data.query.pages).forEach(function (page) {
                    if (page.fileusage) {
                        pages = pages.concat(page.fileusage.map(function (usage) {
                            return usage.title;
                        }));
                    }
                });
                return pages;
            });
        }

        function removeFileReferences(content, fileName) {
        	const fileRegex = new RegExp('^.*\[\[.*?[:/]?' + fileName.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + '(\|.*?)?\]\].*$', 'gim');
            const gallerySectionRegex = /<gallery>([\s\S]*?)<\/gallery>/gi;
            const infoboxRegex = new RegExp('(\|.*?=\s*)' + fileName.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + '(\s*)$', 'gim');
            let updatedContent = content;

            updatedContent = updatedContent.replace(gallerySectionRegex, (match, galleryContent) => {
                const updatedGallery = galleryContent
                    .split('\n')
                    .filter(line => !line.includes(fileName))
                    .join('\n');
                return `<gallery>${updatedGallery}</gallery>`;
            });

            updatedContent = updatedContent.replace(infoboxRegex, (match, beforeEquals, afterEquals) => {
                return beforeEquals + '' + afterEquals;
            });

            updatedContent = updatedContent
                .split('\n')
                .filter(line => !line.match(fileRegex))
                .join('\n');

            return updatedContent;
        }

        function openPageForEditing(page, fileName) {
            new mw.Api().get({
                action: 'query',
                prop: 'revisions',
                titles: page,
                rvslots: 'main',
                rvprop: 'content',
                format: 'json'
            }).then(function (data) {
                const pageContent = Object.values(data.query.pages)[0].revisions[0].slots.main['*'];
                const updatedContent = removeFileReferences(pageContent, fileName);

                const editUrl = new mw.Uri(mw.util.getUrl(page));
                editUrl.extend({ action: 'edit' });

                const win = window.open(editUrl.toString(), '_blank');

                if (win) {
                    win.onload = function () {
                        const textarea = win.document.querySelector('#wpTextbox1');
                        const summary = win.document.querySelector('#wpSummary');

                        if (textarea) {
                            textarea.value = updatedContent;
                        }
                        if (summary) {
                            summary.value = 'Removing references to [[File:' + fileName + ']]';
                        }
                        const previewButton = win.document.querySelector('#wpDiff');
                        if (previewButton) {
                            previewButton.click();
                        }
                    };
                }
            }).catch(function (error) {
                console.error('Error fetching page content:', page, error);
            });
        }

        function removeLinksFromPageAndSave(page, fileName) {
            new mw.Api().get({
                action: 'query',
                prop: 'revisions',
                titles: page,
                rvslots: 'main',
                rvprop: 'content',
                format: 'json'
            }).then(function (data) {
                const pageContent = Object.values(data.query.pages)[0].revisions[0].slots.main['*'];
                const updatedContent = removeFileReferences(pageContent, fileName);

                if (updatedContent === pageContent) {
                    console.warn('No changes detected for page:', page);
                    return;
                }

                new mw.Api().postWithEditToken({
                    action: 'edit',
                    title: page,
                    text: updatedContent,
                    summary: 'Removing references to [[File:' + fileName + ']]'
                }).then(function () {
                    console.log('Updated page:', page);
                }).catch(function (error) {
                    console.error('Error updating page:', page, error);
                });
            }).catch(function (error) {
                console.error('Error fetching page content:', page, error);
            });
        }

        function showDialog(pages, fileName) {
            const dialog = new OO.ui.MessageDialog();
            const windowManager = new OO.ui.WindowManager();
            $(document.body).append(windowManager.$element);
            windowManager.addWindows([dialog]);

            const pageLinks = pages.map(page => {
                return $('<a>')
                    .attr('href', '#')
                    .text(page)
                    .on('click', function (event) {
                        event.preventDefault();
                        openPageForEditing(page, fileName);
                    });
            });

            const content = $('<div>').append(
                $('<p>').text('Select a page to edit or review:'),
                $('<ul>').append(pageLinks.map(link => $('<li>').append(link)))
            );

            windowManager.openWindow(dialog, {
                title: 'Pages linking to file',
                message: content,
                actions: [
                    { action: 'close', label: 'Close', flags: 'safe' }
                ]
            });
        }

        var autoSave = false;
        var button = new OO.ui.ButtonWidget({
            label: 'Delink file',
            icon: 'unlink',
            flags: ['progressive']
        });

        var pageCountLabel = new OO.ui.LabelWidget({
            label: ' (0 pages)',
            classes: ['page-count-label']
        });

        var autoSaveToggle = new OO.ui.ToggleSwitchWidget({
            value: autoSave
        });

        autoSaveToggle.on('change', function (value) {
            autoSave = value;
        });

        $('#mw-content-text').prepend(
            $('<div>').css('margin-bottom', '10px').append(
                $('<div>').append(button.$element, pageCountLabel.$element),
                $('<div>').css('margin-top', '5px').text('Auto-save: ').append(autoSaveToggle.$element)
            )
        );

        button.on('click', function () {
            var fileName = mw.config.get('wgTitle');
            button.setDisabled(true);
            getLocalFileUsage('File:' + fileName).then(function (pages) {
                pageCountLabel.setLabel(' (' + pages.length + ' pages)');
                if (pages.length === 0) {
                    alert('No pages link to this file.');
                } else if (!autoSave) {
                    showDialog(pages, fileName);
                } else {
                    pages.forEach(page => removeLinksFromPageAndSave(page, fileName));
                    alert('Links removed from all pages.');
                }
            }).catch(function (error) {
                console.error('Error fetching file usage:', error);
                alert('An error occurred while fetching file usage.');
            }).always(function () {
                button.setDisabled(false);
            });
        });

        var fileName = mw.config.get('wgTitle');
        getLocalFileUsage('File:' + fileName).then(function (pages) {
            pageCountLabel.setLabel(' (' + pages.length + ' pages)');
        });
    });
});