「利用者:KJP-System/common.js」の版間の差分
ナビゲーションに移動
検索に移動
KJP-System (トーク | 投稿記録) 編集の要約なし |
KJP-System (トーク | 投稿記録) 編集の要約なし |
||
| 35行目: | 35行目: | ||
console.log('KJP Harness: Common.css reloaded.'); | console.log('KJP Harness: Common.css reloaded.'); | ||
}, | |||
wrapEntityPages: function (options) { | |||
var self = this; | |||
var api = new mw.Api(); | |||
var pages = options.pages; | |||
var summary = options.summary || 'KJP Harness: wrap entity pages'; | |||
var dryRun = | |||
typeof options.dryRun === 'boolean' | |||
? options.dryRun | |||
: true; | |||
var startFrom = | |||
'<includeonly>\n__NOTOC__\n__NOEDITSECTION__'; | |||
var startTo = | |||
'<includeonly>\n__NOTOC__\n__NOEDITSECTION__\n<div class="kjp-entity-page">'; | |||
var endFrom = '</includeonly>'; | |||
var endTo = '</div>\n</includeonly>'; | |||
var plan = []; | |||
var backups = []; | |||
var chain = Promise.resolve(); | |||
pages.forEach(function (title) { | |||
chain = chain.then(function () { | |||
return self.getSource(title).then(function (page) { | |||
if (page.text.indexOf('class="kjp-entity-page"') !== -1) { | |||
plan.push({ | |||
title: title, | |||
status: 'SKIP_ALREADY_DONE' | |||
}); | |||
return; | |||
} | |||
var startCount = | |||
page.text.split(startFrom).length - 1; | |||
var endCount = | |||
page.text.split(endFrom).length - 1; | |||
if (startCount !== 1 || endCount !== 1) { | |||
plan.push({ | |||
title: title, | |||
status: 'SKIP_STRUCTURE', | |||
startMatches: startCount, | |||
endMatches: endCount | |||
}); | |||
return; | |||
} | |||
plan.push({ | |||
title: title, | |||
status: dryRun ? 'READY' : 'PATCH' | |||
}); | |||
if (dryRun) { | |||
return; | |||
} | |||
backups.push({ | |||
title: title, | |||
revid: page.revid, | |||
text: page.text | |||
}); | |||
var updated = page.text | |||
.replace(startFrom, startTo) | |||
.replace(endFrom, endTo); | |||
return api.postWithToken('csrf', { | |||
action: 'edit', | |||
title: title, | |||
text: updated, | |||
summary: summary, | |||
minor: true, | |||
basetimestamp: page.timestamp, | |||
formatversion: 2 | |||
}); | |||
}).catch(function (e) { | |||
plan.push({ | |||
title: title, | |||
status: 'ERROR' | |||
}); | |||
console.error(title, e); | |||
}); | |||
}); | |||
}); | |||
return chain.then(function () { | |||
if (!dryRun) { | |||
self.lastBackup = backups; | |||
} | |||
console.table(plan); | |||
if (dryRun) { | |||
console.log( | |||
'DRY RUN: Entityページは変更されていません。' | |||
); | |||
} else { | |||
console.log( | |||
'WRAP完了。バックアップ: ' + | |||
backups.length + | |||
'件' | |||
); | |||
} | |||
return plan; | |||
}); | |||
}, | }, | ||
2026年8月25日 (火) 15:36時点における版
/**
* KJP Harness
* Owner-side maintenance helpers for KPOP JPWiki.
*
* ResourceLoader-compatible version.
* - No automatic edits
* - dryRun by default
* - exact-match validation
* - keeps an in-memory backup of the last patch
*/
window.KJPHarness = {
lastBackup: [],
reloadCommonCss: function () {
var id = 'kjp-harness-common-css';
var old = document.getElementById(id);
if (old) {
old.parentNode.removeChild(old);
}
var link = document.createElement('link');
link.id = id;
link.rel = 'stylesheet';
link.href =
mw.util.getUrl('MediaWiki:Common.css', {
action: 'raw',
ctype: 'text/css',
v: Date.now()
});
document.head.appendChild(link);
console.log('KJP Harness: Common.css reloaded.');
},
wrapEntityPages: function (options) {
var self = this;
var api = new mw.Api();
var pages = options.pages;
var summary = options.summary || 'KJP Harness: wrap entity pages';
var dryRun =
typeof options.dryRun === 'boolean'
? options.dryRun
: true;
var startFrom =
'<includeonly>\n__NOTOC__\n__NOEDITSECTION__';
var startTo =
'<includeonly>\n__NOTOC__\n__NOEDITSECTION__\n<div class="kjp-entity-page">';
var endFrom = '</includeonly>';
var endTo = '</div>\n</includeonly>';
var plan = [];
var backups = [];
var chain = Promise.resolve();
pages.forEach(function (title) {
chain = chain.then(function () {
return self.getSource(title).then(function (page) {
if (page.text.indexOf('class="kjp-entity-page"') !== -1) {
plan.push({
title: title,
status: 'SKIP_ALREADY_DONE'
});
return;
}
var startCount =
page.text.split(startFrom).length - 1;
var endCount =
page.text.split(endFrom).length - 1;
if (startCount !== 1 || endCount !== 1) {
plan.push({
title: title,
status: 'SKIP_STRUCTURE',
startMatches: startCount,
endMatches: endCount
});
return;
}
plan.push({
title: title,
status: dryRun ? 'READY' : 'PATCH'
});
if (dryRun) {
return;
}
backups.push({
title: title,
revid: page.revid,
text: page.text
});
var updated = page.text
.replace(startFrom, startTo)
.replace(endFrom, endTo);
return api.postWithToken('csrf', {
action: 'edit',
title: title,
text: updated,
summary: summary,
minor: true,
basetimestamp: page.timestamp,
formatversion: 2
});
}).catch(function (e) {
plan.push({
title: title,
status: 'ERROR'
});
console.error(title, e);
});
});
});
return chain.then(function () {
if (!dryRun) {
self.lastBackup = backups;
}
console.table(plan);
if (dryRun) {
console.log(
'DRY RUN: Entityページは変更されていません。'
);
} else {
console.log(
'WRAP完了。バックアップ: ' +
backups.length +
'件'
);
}
return plan;
});
},
getSource: function (title) {
var api = new mw.Api();
return api.get({
action: 'query',
prop: 'revisions',
rvprop: 'content|ids|timestamp',
rvslots: 'main',
titles: title,
formatversion: 2
}).then(function (data) {
var page = data.query.pages[0];
var revision = null;
var text = '';
if (page.revisions && page.revisions.length > 0) {
revision = page.revisions[0];
if (
revision.slots &&
revision.slots.main &&
typeof revision.slots.main.content === 'string'
) {
text = revision.slots.main.content;
}
}
return {
title: page.title,
pageid: page.pageid,
revid: revision ? revision.revid : null,
timestamp: revision ? revision.timestamp : null,
text: text
};
});
},
inspect: function (pages, needle) {
var self = this;
var results = [];
var chain = Promise.resolve();
pages.forEach(function (title) {
chain = chain.then(function () {
return self.getSource(title).then(function (page) {
var count = page.text.split(needle).length - 1;
results.push({
title: title,
matches: count,
revid: page.revid
});
}).catch(function (e) {
results.push({
title: title,
matches: 'ERROR',
revid: null
});
console.error(title, e);
});
});
});
return chain.then(function () {
console.table(results);
return results;
});
},
bulkReplace: function (options) {
var self = this;
var api = new mw.Api();
var pages = options.pages;
var from = options.from;
var to = options.to;
var summary = options.summary || 'KJP Harness bulk patch';
var expectedMatches =
typeof options.expectedMatches === 'number'
? options.expectedMatches
: 1;
var dryRun =
typeof options.dryRun === 'boolean'
? options.dryRun
: true;
var plan = [];
var backups = [];
var chain = Promise.resolve();
pages.forEach(function (title) {
chain = chain.then(function () {
return self.getSource(title).then(function (page) {
if (page.text.indexOf(to) !== -1) {
plan.push({
title: title,
status: 'SKIP_ALREADY_DONE'
});
return;
}
var count = page.text.split(from).length - 1;
if (count !== expectedMatches) {
plan.push({
title: title,
status: 'SKIP_MATCH_COUNT',
matches: count
});
return;
}
plan.push({
title: title,
status: dryRun ? 'READY' : 'PATCH'
});
if (dryRun) {
return;
}
backups.push({
title: title,
revid: page.revid,
text: page.text
});
var updated = page.text.replace(from, to);
return api.postWithToken('csrf', {
action: 'edit',
title: title,
text: updated,
summary: summary,
minor: true,
basetimestamp: page.timestamp,
formatversion: 2
});
}).catch(function (e) {
plan.push({
title: title,
status: 'ERROR'
});
console.error(title, e);
});
});
});
return chain.then(function () {
if (!dryRun) {
self.lastBackup = backups;
}
console.table(plan);
if (dryRun) {
console.log('DRY RUN: Wikiは変更されていません。');
} else {
console.log(
'PATCH完了。バックアップ: ' +
backups.length +
'件'
);
}
return plan;
});
}
};
console.log('KJP Harness loaded.');