MediaWiki:Common.js: Difference between revisions

From MDrivenWiki
No edit summary
No edit summary
Line 194: Line 194:


         LoadingDialog.static.name = 'loadingDialog';
         LoadingDialog.static.name = 'loadingDialog';
         LoadingDialog.static.title = 'Deleting Links and Redirects';
         LoadingDialog.static.title = 'Performing Cleanup';


         LoadingDialog.prototype.initialize = function () {
         LoadingDialog.prototype.initialize = function () {

Revision as of 22:44, 7 July 2024

// ==UserScript==
// @name        MediaWiki Clean Delete
// @namespace   MediaWikiScripts
// @description Adds a 'Clean Delete' action link to pages for admins to delete pages and clean up incoming links and redirects.
// ==/UserScript==

mw.loader.using(['mediawiki.api', 'mediawiki.util', 'oojs-ui'], function () {
    function addCleanDeleteLink() {
        if (mw.config.get('wgUserGroups').indexOf('sysop') !== -1) {
            if ($('#ca-cleandelete').length === 0) {
                var $link = $('<div>').attr('id', 'ca-cleandelete').attr('class', 'mw-list-item').append(
                    $('<a>').attr('href', '#').attr('class', 'ca-cleandelete').text('Clean Delete').click(function (e) {
                        e.preventDefault();
                        gatherAllDecisions(mw.config.get('wgPageName'));
                    })
                );
                $('#p-actions .tab-group').append($link);
            }
        }
    }

    function gatherAllDecisions(pageTitle) {
        var decisions = {};
        var windowManager = new OO.ui.WindowManager();
        $('body').append(windowManager.$element);

        askForLinkHandling(pageTitle, decisions, windowManager, function () {
            confirmDeletion(pageTitle, decisions, windowManager, function () {
                showLoadingDialog(windowManager, function (loadingDialog, updateLoadingText) {
                    executeAllActions(pageTitle, decisions, function () {
                        loadingDialog.close();
                        location.reload();
                    }, updateLoadingText);
                });
            });
        });
    }

    function askForLinkHandling(pageTitle, decisions, windowManager, callback) {
        function LinkHandlingDialog(config) {
            LinkHandlingDialog.super.call(this, config);
        }
        OO.inheritClass(LinkHandlingDialog, OO.ui.ProcessDialog);

        LinkHandlingDialog.static.name = 'linkHandlingDialog';
        LinkHandlingDialog.static.title = 'Handle Links and Redirects';
        LinkHandlingDialog.static.actions = [
            { action: 'continue', label: 'Continue', flags: ['primary', 'progressive'] },
            { action: 'cancel', label: 'Cancel', flags: ['safe', 'close'] }
        ];

        LinkHandlingDialog.prototype.initialize = function () {
            LinkHandlingDialog.super.prototype.initialize.apply(this, arguments);

            this.radioSelect = new OO.ui.RadioSelectWidget({
                items: [
                    new OO.ui.RadioOptionWidget({ data: 'delete', label: 'Delete Links and Redirects' }),
                    new OO.ui.RadioOptionWidget({ data: 'change', label: 'Change Target of Links and Redirects' })
                ]
            });

            var fieldset = new OO.ui.FieldsetLayout({
                items: [
                    new OO.ui.FieldLayout(this.radioSelect, {
                        label: 'Specify how you would like to handle all incoming links and redirects:',
                        align: 'top'
                    })
                ]
            });

            this.content = new OO.ui.PanelLayout({
                padded: true,
                expanded: false
            });

            this.content.$element.append(fieldset.$element);
            this.$body.append(this.content.$element);
        };

        LinkHandlingDialog.prototype.getActionProcess = function (action) {
            var dialog = this;
            var process = new OO.ui.Process(function () {
                if (action === 'continue') {
                    var choice = dialog.radioSelect.findSelectedItem();
                    if (choice && choice.data === 'change') {
                        dialog.close().then(function () {
                            askForNewTarget(pageTitle, decisions, windowManager, callback);
                        });
                    } else {
                        decisions.links = '';
                        dialog.close().then(function () {
                            callback();
                        });
                    }
                } else if (action === 'cancel') {
                    dialog.close().then(function () {
                        windowManager.destroy();
                    });
                }
            });
            return process;
        };

        windowManager.addWindows([new LinkHandlingDialog()]);
        windowManager.openWindow('linkHandlingDialog');
    }

    function askForNewTarget(pageTitle, decisions, windowManager, callback) {
        function NewTargetDialog(config) {
            NewTargetDialog.super.call(this, config);
        }
        OO.inheritClass(NewTargetDialog, OO.ui.ProcessDialog);

        NewTargetDialog.static.name = 'newTargetDialog';
        NewTargetDialog.static.title = 'Specify New Target';
        NewTargetDialog.static.actions = [
            { action: 'continue', label: 'Continue', flags: ['primary', 'progressive'] },
            { action: 'cancel', label: 'Cancel', flags: ['safe', 'close'] }
        ];

        NewTargetDialog.prototype.initialize = function () {
            NewTargetDialog.super.prototype.initialize.apply(this, arguments);

            this.input = new OO.ui.TextInputWidget({
                value: '',
                placeholder: 'Enter new target'
            });

            var fieldset = new OO.ui.FieldsetLayout({
                items: [
                    new OO.ui.FieldLayout(this.input, {
                        label: 'Enter the new target page name to update all links and redirects:',
                        align: 'top'
                    })
                ]
            });

            this.content = new OO.ui.PanelLayout({
                padded: true,
                expanded: false
            });

            this.content.$element.append(fieldset.$element);
            this.$body.append(this.content.$element);
        };

        NewTargetDialog.prototype.getActionProcess = function (action) {
            var dialog = this;
            var process = new OO.ui.Process(function () {
                if (action === 'continue') {
                    decisions.links = dialog.input.getValue();
                    dialog.close().then(function () {
                        callback();
                    });
                } else if (action === 'cancel') {
                    dialog.close().then(function () {
                        windowManager.destroy();
                    });
                }
            });
            return process;
        };

        windowManager.addWindows([new NewTargetDialog()]);
        windowManager.openWindow('newTargetDialog');
    }

    function confirmDeletion(pageTitle, decisions, windowManager, callback) {
        var dialog = new OO.ui.MessageDialog();
        windowManager.addWindows([dialog]);

        windowManager.openWindow(dialog, {
            title: 'Confirm Page Deletion',
            message: 'Are you sure you want to delete "' + pageTitle + '" after handling all links and redirects?',
            actions: [
                { label: 'Confirm Deletion', action: 'confirm', flags: ['primary', 'destructive'] },
                { label: 'Cancel', action: 'cancel' }
            ]
        }).closed.then(function (data) {
            if (data.action === 'confirm') {
                decisions.deletion = true;
                callback();
            } else {
                windowManager.destroy();
            }
        });
    }

    function showLoadingDialog(windowManager, callback) {
        function LoadingDialog(config) {
            LoadingDialog.super.call(this, config);
        }
        OO.inheritClass(LoadingDialog, OO.ui.ProcessDialog);

        LoadingDialog.static.name = 'loadingDialog';
        LoadingDialog.static.title = 'Performing Cleanup';

        LoadingDialog.prototype.initialize = function () {
            LoadingDialog.super.prototype.initialize.apply(this, arguments);

            this.loadingLabel = new OO.ui.LabelWidget({ label: 'Starting...' });

            this.content = new OO.ui.PanelLayout({
                padded: true,
                expanded: false
            });

            this.content.$element.append(this.loadingLabel.$element);
            this.$body.append(this.content.$element);
        };

        LoadingDialog.prototype.getActionProcess = function (action) {
            return new OO.ui.Process();
        };

        windowManager.addWindows([new LoadingDialog()]);
        windowManager.openWindow('loadingDialog').then(function (opened) {
            opened.then(function (dialog) {
                callback(dialog, function (text) {
                    dialog.loadingLabel.setLabel(text);
                });
            });
        });
    }

    function executeAllActions(pageTitle, decisions, callback, updateLoadingText) {
        var api = new mw.Api();

        var tasks = [
            function (resolve) {
                if (decisions.links === '') {
                    updateLoadingText('Removing all links and redirects...');
                    removeAllLinksAndRedirects(pageTitle, api, function () {
                        updateLoadingText('All links and redirects removed.');
                        resolve();
                    });
                } else {
                    updateLoadingText('Updating all links and redirects...');
                    updateAllLinksAndRedirects(pageTitle, decisions.links, api, function () {
                        updateLoadingText('All links and redirects updated.');
                        resolve();
                    });
                }
            },
            function (resolve) {
                updateLoadingText('Deleting the page...');
                performDeletion(pageTitle, api, function () {
                    updateLoadingText('Page deleted.');
                    resolve();
                });
            }
        ];

        (function executeTasks(i) {
            if (i < tasks.length) {
                tasks[i](function () {
                    executeTasks(i + 1);
                });
            } else {
                callback();
            }
        })(0);
    }

    function updateAllLinksAndRedirects(pageTitle, newTarget, api, callback) {
        handleLinks(pageTitle, newTarget, 'update', api, function () {
            handleRedirects(pageTitle, newTarget, 'update', api, callback);
        });
    }

    function removeAllLinksAndRedirects(pageTitle, api, callback) {
        handleLinks(pageTitle, '', 'remove', api, function () {
            handleRedirects(pageTitle, '', 'remove', api, callback);
        });
    }

    function handleLinks(pageTitle, newTarget, action, api, callback) {
        api.get({
            action: 'query',
            list: 'backlinks',
            bltitle: pageTitle,
            blfilterredir: 'nonredirects',
            bllimit: 'max'
        }).done(function (data) {
            if (data.query.backlinks) {
                var promises = data.query.backlinks.map(function (link) {
                    return new Promise(function (resolve) {
                        if (action === 'update') {
                            updateLink(link.title, newTarget, api).then(resolve);
                        } else {
                            removeLink(link.title, api).then(resolve);
                        }
                    });
                });
                Promise.all(promises).then(callback);
            } else {
                callback();
            }
        }).fail(function (error) {
            console.error('Error fetching backlinks:', error);
            updateLoadingText('Error fetching backlinks: ' + error);
            callback();
        });
    }

    function handleRedirects(pageTitle, newTarget, action, api, callback) {
        api.get({
            action: 'query',
            list: 'backlinks',
            bltitle: pageTitle,
            blfilterredir: 'redirects',
            bllimit: 'max'
        }).done(function (data) {
            if (data.query.backlinks) {
                var promises = data.query.backlinks.map(function (redirect) {
                    return new Promise(function (resolve) {
                        if (action === 'update') {
                            updateLink(redirect.title, newTarget, api).then(resolve);
                        } else {
                            removeLink(redirect.title, api).then(resolve);
                        }
                    });
                });
                Promise.all(promises).then(callback);
            } else {
                callback();
            }
        }).fail(function (error) {
            console.error('Error fetching redirects:', error);
            updateLoadingText('Error fetching redirects: ' + error);
            callback();
        });
    }

    function updateLink(pageTitle, newTarget, api) {
        return api.postWithToken('csrf', {
            action: 'edit',
            title: pageTitle,
            text: function (text) {
                return text.replace(new RegExp('\\[\\[' + mw.util.escapeRegExp(pageTitle) + '(\\|[^\\]]+)?\\]\\]', 'g'), '[[' + newTarget + '$1]]');
            },
            summary: 'Updated redirect to new target [[' + newTarget + ']]'
        }).fail(function (error) {
            console.error('Error updating link:', error);
        });
    }

    function removeLink(pageTitle, api) {
        return api.postWithToken('csrf', {
            action: 'edit',
            title: pageTitle,
            text: function (text) {
                return text.replace(new RegExp('\\[\\[' + mw.util.escapeRegExp(pageTitle) + '(\\|[^\\]]+)?\\]\\]', 'g'), '');
            },
            summary: 'Removed link to [[' + pageTitle + ']]'
        }).fail(function (error) {
            console.error('Error removing link:', error);
        });
    }

    function performDeletion(pageTitle, api, callback) {
        api.postWithToken('csrf', {
            action: 'delete',
            title: pageTitle,
            reason: 'Automated clean delete by admin'
        }).done(function () {
            console.log('Page deleted:', pageTitle);
            callback();
        }).fail(function (error) {
            console.error('Error during clean deletion:', error);
            callback();
        });
    }

    mw.hook('wikipage.content').add(addCleanDeleteLink);
});
This page was edited 146 days ago on 08/26/2024. What links here