MediaWiki:Common.js: Difference between revisions

From MDrivenWiki
No edit summary
No edit summary
 
(20 intermediate revisions by the same user not shown)
Line 5: Line 5:
// ==/UserScript==
// ==/UserScript==


mw.loader.using(['mediawiki.api', 'mediawiki.util', 'oojs-ui'], function () {
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'jquery'], function () {
     function addCleanDeleteLink() {
     function addCleanDeleteLink() {
         if (mw.config.get('wgUserGroups').indexOf('sysop') !== -1) {
         if (mw.config.get('wgUserGroups').indexOf('sysop') !== -1) {
Line 22: Line 22:
     function gatherAllDecisions(pageTitle) {
     function gatherAllDecisions(pageTitle) {
         var decisions = {};
         var decisions = {};
         var windowManager = new OO.ui.WindowManager();
         showModal('Handle Links and Redirects', '<div class="form-group">' +
        $('body').append(windowManager.$element);
            '<label>Specify how you would like to handle all incoming links and redirects:</label>' +
 
            '<select id="link-handling-select" class="form-control">' +
        askForLinkHandling(pageTitle, decisions, windowManager, function () {
                '<option value="delete">Delete Links and Redirects</option>' +
            confirmDeletion(pageTitle, decisions, windowManager, function () {
                '<option value="change">Change Target of Links and Redirects</option>' +
                 showLoadingDialog(windowManager, function (loadingDialog, updateLoadingText) {
            '</select>' +
                     executeAllActions(pageTitle, decisions, function () {
        '</div>', function () {
                        loadingDialog.close();
            var choice = $('#link-handling-select').val();
                        location.reload();
            if (choice === 'change') {
                     }, updateLoadingText);
                showModal('Specify New Target', '<div class="form-group">' +
                    '<label>Enter the new target page name to update all links and redirects:</label>' +
                    '<input type="text" id="new-target-input" class="form-control" placeholder="Enter new target">' +
                 '</div>', function () {
                     decisions.links = $('#new-target-input').val();
                     confirmDeletion(pageTitle, decisions);
                 });
                 });
             });
             } else {
                decisions.links = '';
                confirmDeletion(pageTitle, decisions);
            }
         });
         });
     }
     }


     function askForLinkHandling(pageTitle, decisions, windowManager, callback) {
     function confirmDeletion(pageTitle, decisions) {
         function LinkHandlingDialog(config) {
         showModal('Confirm Page Deletion', '<p>Are you sure you want to delete "' + pageTitle + '" after handling all links and redirects?</p>', function () {
            LinkHandlingDialog.super.call(this, config);
             decisions.deletion = true;
        }
             showLoadingDialog('Performing Cleanup', function (loadingDialog, updateLoadingText, logDetail) {
        OO.inheritClass(LinkHandlingDialog, OO.ui.ProcessDialog);
                executeAllActions(pageTitle, decisions, function () {
 
                     loadingDialog.close().closed.then(function() {
        LinkHandlingDialog.static.name = 'linkHandlingDialog';
                         location.reload();
        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();
                     });
                     });
                 }
                 }, updateLoadingText, logDetail);
             });
             });
            return process;
         });
         };
 
        windowManager.addWindows([new LinkHandlingDialog()]);
        windowManager.openWindow('linkHandlingDialog');
     }
     }


     function askForNewTarget(pageTitle, decisions, windowManager, callback) {
     function showModal(title, body, onConfirm) {
         function NewTargetDialog(config) {
         var modal = $(
             NewTargetDialog.super.call(this, config);
             '<div class="modal fade" tabindex="-1" role="dialog">' +
        }
                '<div class="modal-dialog" role="document">' +
         OO.inheritClass(NewTargetDialog, OO.ui.ProcessDialog);
                    '<div class="modal-content">' +
                        '<div class="modal-header">' +
                            '<h5 class="modal-title">' + title + '</h5>' +
                            '<button type="button" class="close" data-dismiss="modal" aria-label="Close">' +
                                '<span aria-hidden="true">&times;</span>' +
                            '</button>' +
                        '</div>' +
                        '<div class="modal-body">' + body + '</div>' +
                        '<div class="modal-footer">' +
                            '<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>' +
                            '<button type="button" class="btn btn-primary">Confirm</button>' +
                        '</div>' +
                    '</div>' +
                '</div>' +
            '</div>'
         );


         NewTargetDialog.static.name = 'newTargetDialog';
         modal.find('.btn-primary').click(function () {
        NewTargetDialog.static.title = 'Specify New Target';
             onConfirm();
        NewTargetDialog.static.actions = [
             modal.modal('hide');
             { action: 'continue', label: 'Continue', flags: ['primary', 'progressive'] },
         });
             { action: 'cancel', label: 'Cancel', flags: ['safe', 'close'] }
         ];


         NewTargetDialog.prototype.initialize = function () {
         modal.on('hidden.bs.modal', function () {
             NewTargetDialog.super.prototype.initialize.apply(this, arguments);
             modal.remove();
        });


            this.input = new OO.ui.TextInputWidget({
        $('body').append(modal);
                value: '',
         modal.modal('show');
                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) {
     function showLoadingDialog(message, callback) {
         var dialog = new OO.ui.MessageDialog();
         var modal = $(
         windowManager.addWindows([dialog]);
            '<div class="modal fade" tabindex="-1" role="dialog">' +
                '<div class="modal-dialog" role="document">' +
                    '<div class="modal-content">' +
                        '<div class="modal-header">' +
                            '<h5 class="modal-title">Performing Cleanup</h5>' +
                        '</div>' +
                        '<div class="modal-body">' +
                            '<p id="loading-text">' + message + '</p>' +
                            '<ul id="loading-log"></ul>' +
                        '</div>' +
                    '</div>' +
                '</div>' +
            '</div>'
         );


         windowManager.openWindow(dialog, {
         $('body').append(modal);
            title: 'Confirm Page Deletion',
         modal.modal('show');
            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()]);
         callback(modal, function (text) {
        windowManager.openWindow('loadingDialog').then(function (opened) {
            $('#loading-text').text(text);
             opened.then(function (dialog) {
             $('#loading-log').append('<li>' + text + '</li>');
                callback(dialog, function (text) {
        }, function (detail) {
                    dialog.loadingLabel.setLabel(text);
            $('#loading-log').append('<li>' + detail + '</li>');
                });
            });
         });
         });
     }
     }


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


         function handleLinksAndRedirects(action, newTarget, next) {
         function handleLinksAndRedirects(action, newTarget, next) {
             updateLoadingText(action === 'update' ? 'Updating all links and redirects...' : 'Removing all links and redirects...');
             updateLoadingText('Fetching backlinks...');
             api.get({
             api.get({
                 action: 'query',
                 action: 'query',
                 list: 'backlinks',
                 list: 'backlinks',
                 bltitle: pageTitle,
                 bltitle: pageTitle,
                 bllimit: 'max'
                 bllimit: 'max',
                blfilterredir: 'all' // Include both redirects and non-redirects
             }).done(function (data) {
             }).done(function (data) {
                logDetail('Backlinks fetched.');
                 if (data.query.backlinks) {
                 if (data.query.backlinks) {
                     var promises = data.query.backlinks.map(function (link) {
                     var promises = $.map(data.query.backlinks, function (link) {
                         return new Promise(function (resolve) {
                         return new $.Deferred(function (defer) {
                             if (link.ns === 0) {
                             api.get({
                                 if (action === 'update') {
                                 action: 'parse',
                                    updateLink(link.title, newTarget, api).then(resolve);
                                page: link.title,
                                 } else {
                                prop: 'wikitext'
                                    removeLink(link.title, api).then(resolve);
                            }).done(function (linkData) {
                                 }
                                 var isRedirect = linkData.parse.wikitext['*'].trim().startsWith('#REDIRECT');
                            } else if (link.ns === 10) {
                                 if (isRedirect) {
                                if (action === 'update') {
                                    if (action === 'update') {
                                    updateLink(link.title, newTarget, api).then(resolve);
                                        updateRedirectTarget(link.title, newTarget, api, defer.resolve, logDetail);
                                    } else {
                                        logDetail('Backlink ' + link.title + ' is a redirect. Deleting...');
                                        performDeletion(link.title, defer.resolve, logDetail);
                                    }
                                 } else {
                                 } else {
                                     removeRedirect(link.title, api).then(resolve);
                                     if (action === 'update') {
                                        updateLinkInPage(link.title, pageTitle, newTarget, api, defer.resolve, logDetail);
                                    } else {
                                        removeLinkFromPage(link.title, pageTitle, api, defer.resolve, logDetail);
                                    }
                                 }
                                 }
                             } else {
                             }).fail(function (error) {
                                 resolve();
                                 logDetail('Error checking if backlink ' + link.title + ' is a redirect: ' + error);
                             }
                                defer.resolve();
                         });
                             });
                         }).promise();
                     });
                     });
                     Promise.all(promises).then(function () {
                     $.when.apply($, promises).then(function () {
                         updateLoadingText(action === 'update' ? 'All links and redirects updated.' : 'All links and redirects removed.');
                         updateLoadingText(action === 'update' ? 'All links updated.' : 'All links removed.');
                         next();
                         next();
                     });
                     });
Line 263: Line 170:
                 }
                 }
             }).fail(function (error) {
             }).fail(function (error) {
                console.error('Error fetching backlinks:', error);
                 updateLoadingText('Error fetching backlinks: ' + error);
                 updateLoadingText('Error fetching backlinks: ' + error);
                 next();
                 next();
Line 269: Line 175:
         }
         }


         function updateLink(pageTitle, newTarget, api) {
         function updateLinkInPage(linkedPageTitle, originalPageTitle, newTarget, api, resolve, logDetail) {
            api.get({
                action: 'parse',
                page: linkedPageTitle,
                prop: 'wikitext'
            }).then(function (data) {
                var wikitext = data.parse.wikitext['*'];
                var regex = new RegExp('\\[\\[' + originalPageTitle.replace(/[\[\]]/g, '\\$&') + '(\\|[^\\]]+)?\\]\\]', 'g');
                var newWikitext = wikitext.replace(regex, '[[' + newTarget + '$1]]');
                if (wikitext !== newWikitext) {
                    logDetail('Updating link in ' + linkedPageTitle);
                } else {
                    logDetail('No link found in ' + linkedPageTitle);
                }
                return api.postWithToken('csrf', {
                    action: 'edit',
                    title: linkedPageTitle,
                    text: newWikitext,
                    summary: 'Updated link to [[' + newTarget + ']]'
                }).then(function (response) {
                    console.log('API response from updating link: ', response);
                    if (response.edit && response.edit.result === 'Success' && !response.edit.nochange) {
                        logDetail('Successfully updated link in ' + linkedPageTitle);
                    } else {
                        logDetail('No changes made to link in ' + linkedPageTitle);
                    }
                    resolve();
                }).fail(function (error) {
                    logDetail('Error updating link in ' + linkedPageTitle + ': ' + error);
                    resolve();
                });
            }).fail(function (error) {
                logDetail('Error fetching page content for ' + linkedPageTitle + ': ' + error);
                resolve();
            });
        }
 
        function updateRedirectTarget(redirectPageTitle, newTarget, api, resolve, logDetail) {
            logDetail('Updating redirect target in ' + redirectPageTitle);
             return api.postWithToken('csrf', {
             return api.postWithToken('csrf', {
                 action: 'edit',
                 action: 'edit',
                 title: pageTitle,
                 title: redirectPageTitle,
                 text: function (text) {
                 text: '#REDIRECT [[' + newTarget + ']]',
                    return text.replace(new RegExp('\\[\\[' + mw.util.escapeRegExp(pageTitle) + '(\\|[^\\]]+)?\\]\\]', 'g'), '[[' + newTarget + '$1]]');
                summary: 'Updated redirect to [[' + newTarget + ']]'
                 },
            }).then(function (response) {
                 summary: 'Updated link to new target [[' + newTarget + ']]'
                 console.log('API response from updating redirect: ', response);
                 if (response.edit && response.edit.result === 'Success' && !response.edit.nochange) {
                    logDetail('Successfully updated redirect in ' + redirectPageTitle);
                } else {
                    logDetail('No changes made to redirect in ' + redirectPageTitle);
                }
                resolve();
             }).fail(function (error) {
             }).fail(function (error) {
                 console.error('Error updating link:', error);
                 logDetail('Error updating redirect in ' + redirectPageTitle + ': ' + error);
                resolve();
             });
             });
         }
         }


         function removeLink(pageTitle, api) {
         function removeLinkFromPage(linkedPageTitle, originalPageTitle, api, resolve, logDetail) {
             return api.postWithToken('csrf', {
             api.get({
                 action: 'edit',
                 action: 'parse',
                 title: pageTitle,
                 page: linkedPageTitle,
                 text: function (text) {
                 prop: 'wikitext'
                    return text.replace(new RegExp('\\[\\[' + mw.util.escapeRegExp(pageTitle) + '(\\|[^\\]]+)?\\]\\]', 'g'), '');
            }).then(function (data) {
                 },
                var wikitext = data.parse.wikitext['*'];
                summary: 'Removed link to [[' + pageTitle + ']]'
                var regex = new RegExp('\\[\\[' + originalPageTitle.replace(/[\[\]]/g, '\\$&') + '(\\|[^\\]]+)?\\]\\]', 'g');
                var newWikitext = wikitext.replace(regex, function(match, p1) {
                    return p1 ? p1.substring(1) : originalPageTitle;
                });
                if (wikitext !== newWikitext) {
                    logDetail('Removing link from ' + linkedPageTitle);
                 } else {
                    logDetail('No link found in ' + linkedPageTitle);
                }
                return api.postWithToken('csrf', {
                    action: 'edit',
                    title: linkedPageTitle,
                    text: newWikitext,
                    summary: 'Removed link to [[' + originalPageTitle + ']]'
                }).then(function (response) {
                    console.log('API response from removing link: ', response);
                    if (response.edit && response.edit.result === 'Success' && !response.edit.nochange) {
                        logDetail('Successfully removed link from ' + linkedPageTitle);
                    } else {
                        logDetail('No changes made to link in ' + linkedPageTitle);
                    }
                    resolve();
                }).fail(function (error) {
                    logDetail('Error removing link from ' + linkedPageTitle + ': ' + error);
                    resolve();
                });
             }).fail(function (error) {
             }).fail(function (error) {
                 console.error('Error removing link:', error);
                 logDetail('Error fetching page content for ' + linkedPageTitle + ': ' + error);
                resolve();
             });
             });
         }
         }


         function removeRedirect(pageTitle, api) {
         function handleRedirects(next) {
             return api.postWithToken('csrf', {
             updateLoadingText('Fetching redirects...');
                 action: 'delete',
            api.get({
                 title: pageTitle,
                 action: 'query',
                 reason: 'Removed redirect to [[' + pageTitle + ']]'
                 titles: pageTitle,
                 redirects: true
            }).done(function (data) {
                logDetail('Redirects fetched.');
                var redirects = data.query.pages[Object.keys(data.query.pages)[0]].redirects;
                if (redirects) {
                    var promises = $.map(redirects, function (redirect) {
                        return new $.Deferred(function (defer) {
                            logDetail('Found redirect: ' + redirect.title);
                            performDeletion(redirect.title, defer.resolve, logDetail);
                        }).promise();
                    });
                    $.when.apply($, promises).then(function () {
                        next();
                    });
                } else {
                    next();
                }
             }).fail(function (error) {
             }).fail(function (error) {
                 console.error('Error removing redirect:', error);
                 updateLoadingText('Error fetching redirects: ' + error);
                next();
             });
             });
         }
         }


         function performDeletion(pageTitle, next) {
         function performDeletion(pageTitle, resolve, logDetail) {
             updateLoadingText('Deleting the page...');
             updateLoadingText('Deleting the page: ' + pageTitle);
             api.postWithToken('csrf', {
             api.postWithToken('csrf', {
                 action: 'delete',
                 action: 'delete',
                 title: pageTitle,
                 title: pageTitle,
                 reason: 'Automated clean delete by admin'
                 reason: 'Automated clean delete by admin'
             }).done(function () {
             }).done(function (response) {
                 updateLoadingText('Page deleted.');
                 console.log('API response from deleting page: ', response);
                 next();
                if (response.delete && response.delete.title) {
                    logDetail('Successfully deleted page: ' + response.delete.title);
                } else {
                    logDetail('No deletion performed for ' + pageTitle);
                 }
                resolve();
             }).fail(function (error) {
             }).fail(function (error) {
                 console.error('Error during clean deletion:', error);
                 logDetail('Error during clean deletion of ' + pageTitle + ': ' + error);
                updateLoadingText('Error during clean deletion: ' + error);
                 resolve();
                 next();
             });
             });
         }
         }
Line 330: Line 329:
             },
             },
             function (resolve) {
             function (resolve) {
                 performDeletion(pageTitle, resolve);
                 if (decisions.deletion) {
                    handleRedirects(resolve);
                } else {
                    resolve();
                }
            },
            function (resolve) {
                if (decisions.deletion) {
                    performDeletion(pageTitle, resolve, logDetail);
                } else {
                    resolve();
                }
             }
             }
         ];
         ];
Line 347: Line 357:
     mw.hook('wikipage.content').add(addCleanDeleteLink);
     mw.hook('wikipage.content').add(addCleanDeleteLink);
});
});
(function() {
    // Function to remove special characters from the search term
    function sanitizeSearchTerm(term) {
        return term.replace(/[^a-zA-Z0-9\s]/g, ''); // Keep only alphanumeric characters and spaces
    }
    // Save the original fetch function
    var originalFetch = window.fetch;
    // Override the fetch function
    window.fetch = function() {
        var url = arguments[0];
        var options = arguments[1] || {};
        // Check if the URL is the target API endpoint
        if (typeof url === 'string' && url.indexOf('action=bs-extendedsearch-query') !== -1) {
            // Decode the URL to manipulate it
            var decodedUrl = decodeURIComponent(url);
            // Find the search term in the URL
            var searchTermMatch = decodedUrl.match(/searchTerm=([^&]*)/);
            if (searchTermMatch && searchTermMatch[1]) {
                var originalSearchTerm = searchTermMatch[1];
                // Sanitize the search term
                var sanitizedSearchTerm = sanitizeSearchTerm(originalSearchTerm);
                // Replace the original search term with the sanitized one
                decodedUrl = decodedUrl.replace(originalSearchTerm, sanitizedSearchTerm);
                // Re-encode the URL
                url = encodeURI(decodedUrl);
            }
        }
        // Call the original fetch function with the modified URL
        return originalFetch.apply(this, [url, options]);
    };
})();

Latest revision as of 23:50, 25 August 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', 'jquery'], 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 = {};
        showModal('Handle Links and Redirects', '<div class="form-group">' +
            '<label>Specify how you would like to handle all incoming links and redirects:</label>' +
            '<select id="link-handling-select" class="form-control">' +
                '<option value="delete">Delete Links and Redirects</option>' +
                '<option value="change">Change Target of Links and Redirects</option>' +
            '</select>' +
        '</div>', function () {
            var choice = $('#link-handling-select').val();
            if (choice === 'change') {
                showModal('Specify New Target', '<div class="form-group">' +
                    '<label>Enter the new target page name to update all links and redirects:</label>' +
                    '<input type="text" id="new-target-input" class="form-control" placeholder="Enter new target">' +
                '</div>', function () {
                    decisions.links = $('#new-target-input').val();
                    confirmDeletion(pageTitle, decisions);
                });
            } else {
                decisions.links = '';
                confirmDeletion(pageTitle, decisions);
            }
        });
    }

    function confirmDeletion(pageTitle, decisions) {
        showModal('Confirm Page Deletion', '<p>Are you sure you want to delete "' + pageTitle + '" after handling all links and redirects?</p>', function () {
            decisions.deletion = true;
            showLoadingDialog('Performing Cleanup', function (loadingDialog, updateLoadingText, logDetail) {
                executeAllActions(pageTitle, decisions, function () {
                    loadingDialog.close().closed.then(function() {
                        location.reload();
                    });
                }, updateLoadingText, logDetail);
            });
        });
    }

    function showModal(title, body, onConfirm) {
        var modal = $(
            '<div class="modal fade" tabindex="-1" role="dialog">' +
                '<div class="modal-dialog" role="document">' +
                    '<div class="modal-content">' +
                        '<div class="modal-header">' +
                            '<h5 class="modal-title">' + title + '</h5>' +
                            '<button type="button" class="close" data-dismiss="modal" aria-label="Close">' +
                                '<span aria-hidden="true">&times;</span>' +
                            '</button>' +
                        '</div>' +
                        '<div class="modal-body">' + body + '</div>' +
                        '<div class="modal-footer">' +
                            '<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>' +
                            '<button type="button" class="btn btn-primary">Confirm</button>' +
                        '</div>' +
                    '</div>' +
                '</div>' +
            '</div>'
        );

        modal.find('.btn-primary').click(function () {
            onConfirm();
            modal.modal('hide');
        });

        modal.on('hidden.bs.modal', function () {
            modal.remove();
        });

        $('body').append(modal);
        modal.modal('show');
    }

    function showLoadingDialog(message, callback) {
        var modal = $(
            '<div class="modal fade" tabindex="-1" role="dialog">' +
                '<div class="modal-dialog" role="document">' +
                    '<div class="modal-content">' +
                        '<div class="modal-header">' +
                            '<h5 class="modal-title">Performing Cleanup</h5>' +
                        '</div>' +
                        '<div class="modal-body">' +
                            '<p id="loading-text">' + message + '</p>' +
                            '<ul id="loading-log"></ul>' +
                        '</div>' +
                    '</div>' +
                '</div>' +
            '</div>'
        );

        $('body').append(modal);
        modal.modal('show');

        callback(modal, function (text) {
            $('#loading-text').text(text);
            $('#loading-log').append('<li>' + text + '</li>');
        }, function (detail) {
            $('#loading-log').append('<li>' + detail + '</li>');
        });
    }

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

        function handleLinksAndRedirects(action, newTarget, next) {
            updateLoadingText('Fetching backlinks...');
            api.get({
                action: 'query',
                list: 'backlinks',
                bltitle: pageTitle,
                bllimit: 'max',
                blfilterredir: 'all' // Include both redirects and non-redirects
            }).done(function (data) {
                logDetail('Backlinks fetched.');
                if (data.query.backlinks) {
                    var promises = $.map(data.query.backlinks, function (link) {
                        return new $.Deferred(function (defer) {
                            api.get({
                                action: 'parse',
                                page: link.title,
                                prop: 'wikitext'
                            }).done(function (linkData) {
                                var isRedirect = linkData.parse.wikitext['*'].trim().startsWith('#REDIRECT');
                                if (isRedirect) {
                                    if (action === 'update') {
                                        updateRedirectTarget(link.title, newTarget, api, defer.resolve, logDetail);
                                    } else {
                                        logDetail('Backlink ' + link.title + ' is a redirect. Deleting...');
                                        performDeletion(link.title, defer.resolve, logDetail);
                                    }
                                } else {
                                    if (action === 'update') {
                                        updateLinkInPage(link.title, pageTitle, newTarget, api, defer.resolve, logDetail);
                                    } else {
                                        removeLinkFromPage(link.title, pageTitle, api, defer.resolve, logDetail);
                                    }
                                }
                            }).fail(function (error) {
                                logDetail('Error checking if backlink ' + link.title + ' is a redirect: ' + error);
                                defer.resolve();
                            });
                        }).promise();
                    });
                    $.when.apply($, promises).then(function () {
                        updateLoadingText(action === 'update' ? 'All links updated.' : 'All links removed.');
                        next();
                    });
                } else {
                    next();
                }
            }).fail(function (error) {
                updateLoadingText('Error fetching backlinks: ' + error);
                next();
            });
        }

        function updateLinkInPage(linkedPageTitle, originalPageTitle, newTarget, api, resolve, logDetail) {
            api.get({
                action: 'parse',
                page: linkedPageTitle,
                prop: 'wikitext'
            }).then(function (data) {
                var wikitext = data.parse.wikitext['*'];
                var regex = new RegExp('\\[\\[' + originalPageTitle.replace(/[\[\]]/g, '\\$&') + '(\\|[^\\]]+)?\\]\\]', 'g');
                var newWikitext = wikitext.replace(regex, '[[' + newTarget + '$1]]');
                if (wikitext !== newWikitext) {
                    logDetail('Updating link in ' + linkedPageTitle);
                } else {
                    logDetail('No link found in ' + linkedPageTitle);
                }
                return api.postWithToken('csrf', {
                    action: 'edit',
                    title: linkedPageTitle,
                    text: newWikitext,
                    summary: 'Updated link to [[' + newTarget + ']]'
                }).then(function (response) {
                    console.log('API response from updating link: ', response);
                    if (response.edit && response.edit.result === 'Success' && !response.edit.nochange) {
                        logDetail('Successfully updated link in ' + linkedPageTitle);
                    } else {
                        logDetail('No changes made to link in ' + linkedPageTitle);
                    }
                    resolve();
                }).fail(function (error) {
                    logDetail('Error updating link in ' + linkedPageTitle + ': ' + error);
                    resolve();
                });
            }).fail(function (error) {
                logDetail('Error fetching page content for ' + linkedPageTitle + ': ' + error);
                resolve();
            });
        }

        function updateRedirectTarget(redirectPageTitle, newTarget, api, resolve, logDetail) {
            logDetail('Updating redirect target in ' + redirectPageTitle);
            return api.postWithToken('csrf', {
                action: 'edit',
                title: redirectPageTitle,
                text: '#REDIRECT [[' + newTarget + ']]',
                summary: 'Updated redirect to [[' + newTarget + ']]'
            }).then(function (response) {
                console.log('API response from updating redirect: ', response);
                if (response.edit && response.edit.result === 'Success' && !response.edit.nochange) {
                    logDetail('Successfully updated redirect in ' + redirectPageTitle);
                } else {
                    logDetail('No changes made to redirect in ' + redirectPageTitle);
                }
                resolve();
            }).fail(function (error) {
                logDetail('Error updating redirect in ' + redirectPageTitle + ': ' + error);
                resolve();
            });
        }

        function removeLinkFromPage(linkedPageTitle, originalPageTitle, api, resolve, logDetail) {
            api.get({
                action: 'parse',
                page: linkedPageTitle,
                prop: 'wikitext'
            }).then(function (data) {
                var wikitext = data.parse.wikitext['*'];
                var regex = new RegExp('\\[\\[' + originalPageTitle.replace(/[\[\]]/g, '\\$&') + '(\\|[^\\]]+)?\\]\\]', 'g');
                var newWikitext = wikitext.replace(regex, function(match, p1) {
                    return p1 ? p1.substring(1) : originalPageTitle;
                });
                if (wikitext !== newWikitext) {
                    logDetail('Removing link from ' + linkedPageTitle);
                } else {
                    logDetail('No link found in ' + linkedPageTitle);
                }
                return api.postWithToken('csrf', {
                    action: 'edit',
                    title: linkedPageTitle,
                    text: newWikitext,
                    summary: 'Removed link to [[' + originalPageTitle + ']]'
                }).then(function (response) {
                    console.log('API response from removing link: ', response);
                    if (response.edit && response.edit.result === 'Success' && !response.edit.nochange) {
                        logDetail('Successfully removed link from ' + linkedPageTitle);
                    } else {
                        logDetail('No changes made to link in ' + linkedPageTitle);
                    }
                    resolve();
                }).fail(function (error) {
                    logDetail('Error removing link from ' + linkedPageTitle + ': ' + error);
                    resolve();
                });
            }).fail(function (error) {
                logDetail('Error fetching page content for ' + linkedPageTitle + ': ' + error);
                resolve();
            });
        }

        function handleRedirects(next) {
            updateLoadingText('Fetching redirects...');
            api.get({
                action: 'query',
                titles: pageTitle,
                redirects: true
            }).done(function (data) {
                logDetail('Redirects fetched.');
                var redirects = data.query.pages[Object.keys(data.query.pages)[0]].redirects;
                if (redirects) {
                    var promises = $.map(redirects, function (redirect) {
                        return new $.Deferred(function (defer) {
                            logDetail('Found redirect: ' + redirect.title);
                            performDeletion(redirect.title, defer.resolve, logDetail);
                        }).promise();
                    });
                    $.when.apply($, promises).then(function () {
                        next();
                    });
                } else {
                    next();
                }
            }).fail(function (error) {
                updateLoadingText('Error fetching redirects: ' + error);
                next();
            });
        }

        function performDeletion(pageTitle, resolve, logDetail) {
            updateLoadingText('Deleting the page: ' + pageTitle);
            api.postWithToken('csrf', {
                action: 'delete',
                title: pageTitle,
                reason: 'Automated clean delete by admin'
            }).done(function (response) {
                console.log('API response from deleting page: ', response);
                if (response.delete && response.delete.title) {
                    logDetail('Successfully deleted page: ' + response.delete.title);
                } else {
                    logDetail('No deletion performed for ' + pageTitle);
                }
                resolve();
            }).fail(function (error) {
                logDetail('Error during clean deletion of ' + pageTitle + ': ' + error);
                resolve();
            });
        }

        var tasks = [
            function (resolve) {
                if (decisions.links === '') {
                    handleLinksAndRedirects('remove', '', resolve);
                } else {
                    handleLinksAndRedirects('update', decisions.links, resolve);
                }
            },
            function (resolve) {
                if (decisions.deletion) {
                    handleRedirects(resolve);
                } else {
                    resolve();
                }
            },
            function (resolve) {
                if (decisions.deletion) {
                    performDeletion(pageTitle, resolve, logDetail);
                } else {
                    resolve();
                }
            }
        ];

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

    mw.hook('wikipage.content').add(addCleanDeleteLink);
});


(function() {
    // Function to remove special characters from the search term
    function sanitizeSearchTerm(term) {
        return term.replace(/[^a-zA-Z0-9\s]/g, ''); // Keep only alphanumeric characters and spaces
    }

    // Save the original fetch function
    var originalFetch = window.fetch;

    // Override the fetch function
    window.fetch = function() {
        var url = arguments[0];
        var options = arguments[1] || {};

        // Check if the URL is the target API endpoint
        if (typeof url === 'string' && url.indexOf('action=bs-extendedsearch-query') !== -1) {
            // Decode the URL to manipulate it
            var decodedUrl = decodeURIComponent(url);

            // Find the search term in the URL
            var searchTermMatch = decodedUrl.match(/searchTerm=([^&]*)/);
            if (searchTermMatch && searchTermMatch[1]) {
                var originalSearchTerm = searchTermMatch[1];

                // Sanitize the search term
                var sanitizedSearchTerm = sanitizeSearchTerm(originalSearchTerm);

                // Replace the original search term with the sanitized one
                decodedUrl = decodedUrl.replace(originalSearchTerm, sanitizedSearchTerm);

                // Re-encode the URL
                url = encodeURI(decodedUrl);
            }
        }

        // Call the original fetch function with the modified URL
        return originalFetch.apply(this, [url, options]);
    };
})();
This page was edited 146 days ago on 08/26/2024. What links here