User:BZPN/FindOutdatedPages.js
From Test 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)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
//<nowiki>
// Author:
// License:
mw.loader.using(['oojs-ui', 'mediawiki.util']).done(function () {
var scriptActivationLink = mw.util.addPortletLink(
'p-tb',
'#',
'Find Outdated Test Pages and Files',
'pt-find-outdated-pages',
'Finds and manages outdated test pages and files.',
null,
'#pt-adminlinks'
);
$(scriptActivationLink).click(function () {
var progressBar = new OO.ui.ProgressBarWidget();
var progressField = new OO.ui.FieldLayout(
progressBar,
{
label: "Searching for outdated pages and files...",
align: 'top'
}
);
var textPanel = new OO.ui.PanelLayout({
expanded: false,
framed: false,
padded: false,
});
var canDelete = false;
var outdatedPagesAndFiles = [];
function OutdatedPagesProc(config) {
OutdatedPagesProc.super.call(this, config);
}
OO.inheritClass(OutdatedPagesProc, OO.ui.ProcessDialog);
OutdatedPagesProc.static.name = "Outdated Pages and Files Process";
OutdatedPagesProc.static.title = "List of Outdated Test Pages and Files:";
OutdatedPagesProc.static.actions = [
{
action: "delete",
label: "Delete Selected",
flags: "primary"
},
{
label: "Cancel",
flags: "safe"
}
];
OutdatedPagesProc.prototype.initialize = function () {
OutdatedPagesProc.super.prototype.initialize.apply(this, arguments);
this.content = new OO.ui.FieldsetLayout();
this.content.addItems([progressField]);
this.content.$element.css('padding', '1em');
this.$body.append(this.content.$element);
};
OutdatedPagesProc.prototype.getActionProcess = function (action) {
var dialog = this;
if (action && canDelete) {
return new OO.ui.Process(function () {
if (action == "delete") {
deleteSelectedItems();
}
dialog.close({
action: action
});
});
}
return OutdatedPagesProc.super.prototype.getActionProcess.call(this, action);
};
OutdatedPagesProc.prototype.getBodyHeight = function () {
return this.content.$element.outerHeight(true);
};
var windowManager = new OO.ui.WindowManager();
var outdatedPagesProc = new OutdatedPagesProc({
size: "large"
});
windowManager.addWindows([outdatedPagesProc]);
$('body').append(windowManager.$element);
windowManager.openWindow(outdatedPagesProc);
var totalPages = 0;
var checkedPages = 0;
// Fetch list of pages from specified namespaces
function fetchPages(namespaces) {
namespaces.forEach(function (ns) {
$.getJSON(
mw.util.wikiScript('api'),
{
format: 'json',
action: 'query',
list: 'allpages',
apnamespace: ns, // Specify namespace
aplimit: 100, // Fetch up to 100 pages per request
apfilterredir: 'nonredirect' // Exclude redirects
}
).done(function (data) {
var pages = data.query.allpages;
totalPages += pages.length;
pages.forEach(function (page) {
checkPage(page);
});
}).fail(function () {
console.log("Error fetching pages from namespace: " + ns);
});
});
}
// Fetch pages from specified namespaces
fetchPages([0, 10, 2, 4]); // Main namespace (0), Template (10), User (2), TestWiki (4)
$.getJSON(
mw.util.wikiScript('api'),
{
format: 'json',
action: 'query',
list: 'allimages',
ailimit: 100
}
).done(function (data) {
var files = data.query.allimages;
totalPages += files.length;
files.forEach(function (file) {
checkFile(file);
});
}).fail(function () {
console.log("Error fetching files.");
});
function checkPage(page) {
$.getJSON(
mw.util.wikiScript('api'),
{
format: 'json',
action: 'query',
prop: 'revisions',
titles: page.title,
rvprop: 'timestamp|content',
rvslots: 'main',
rvlimit: 1
}
).done(function (data) {
var pageData = Object.values(data.query.pages)[0];
var content = pageData.revisions[0].slots.main['*'];
var isBlanked = content.trim() === "";
if (isBlanked) {
outdatedPagesAndFiles.push({ title: page.title, status: "blanked page" });
} else {
var lastEdit = new Date(pageData.revisions[0].timestamp);
var now = new Date();
var tenHoursAgo = new Date(now - 10 * 60 * 60 * 1000);
if (lastEdit < tenHoursAgo && content.length < 10) {
outdatedPagesAndFiles.push({ title: page.title, status: "short content" });
}
}
checkedPages++;
progressBar.setProgress(parseInt((checkedPages / totalPages) * 100));
if (checkedPages === totalPages) {
listOutdatedItems();
}
}).fail(function () {
console.log("Error checking page: " + page.title);
});
}
function checkFile(file) {
$.getJSON(
mw.util.wikiScript('api'),
{
format: 'json',
action: 'query',
titles: 'File:' + file.name,
prop: 'imageinfo|revisions|categories',
iiprop: 'timestamp|user|comment',
rvprop: 'content',
rvlimit: 1,
clshow: '!hidden',
cllimit: 'max'
}
).done(function (data) {
var fileData = Object.values(data.query.pages)[0];
var description = fileData.revisions[0]['*'];
var categories = fileData.categories ? fileData.categories.map(cat => cat.title) : [];
var outOfScope = !description.includes('{{Information}}') && !categories.includes('Category:Non-test files');
var isMaintScript = description.includes('Maintenance script') || description.includes('Uploaded by Maintenance script');
if (outOfScope && !isMaintScript) {
checkFileUsage(file);
} else {
checkedPages++;
}
}).fail(function () {
console.log("Error checking file: " + file.name);
});
}
function checkFileUsage(file) {
$.getJSON(
mw.util.wikiScript('api'),
{
format: 'json',
action: 'query',
list: 'imageusage',
iutitle: 'File:' + file.name,
iulimit: 1
}
).done(function (data) {
if (data.query.imageusage.length === 0) {
outdatedPagesAndFiles.push({ title: 'File:' + file.name, status: 'unused file' });
}
checkedPages++;
progressBar.setProgress(parseInt((checkedPages / totalPages) * 100));
if (checkedPages === totalPages) {
listOutdatedItems();
}
}).fail(function () {
console.log("Error checking file usage: " + file.name);
});
}
function listOutdatedItems() {
outdatedPagesAndFiles.sort((a, b) => a.title.localeCompare(b.title)).forEach(function (item) {
var itemLink = `<li><input type="checkbox" class="page-checkbox" data-page="${item.title}"> <a href="/wiki/${item.title}">${item.title}</a> <span style="color: red;">(${item.status})</span></li>`;
textPanel.$element.append(itemLink);
});
outdatedPagesProc.content.clearItems();
outdatedPagesProc.content.addItems([textPanel]);
outdatedPagesProc.updateSize();
canDelete = true;
}
function deleteSelectedItems() {
var selectedItems = [];
$('.page-checkbox:checked').each(function () {
selectedItems.push($(this).data('page'));
});
if (selectedItems.length === 0) {
alert("No pages or files selected for deletion.");
return;
}
selectedItems.forEach(function (item) {
$.post(
mw.util.wikiScript('api'),
{
format: 'json',
action: 'delete',
title: item,
reason: 'Outdated test page or out of scope file - automated cleanup',
token: mw.user.tokens.get('csrfToken')
}
).done(function () {
console.log("Deleted item: " + item);
}).fail(function () {
console.log("Error deleting item: " + item);
});
});
alert("Selected pages and files have been deleted.");
}
});
});
//</nowiki>