利用者:KJP-System/common.js
注意: 保存後、変更を確認するにはブラウザーのキャッシュを消去する必要がある場合があります。
- Firefox / Safari: Shift を押しながら 再読み込み をクリックするか、Ctrl-F5 または Ctrl-R を押してください (Mac では ⌘-R)
- Google Chrome: Ctrl-Shift-R を押してください (Mac では ⌘-Shift-R)
- Microsoft Edge: Ctrl を押しながら 最新の情報に更新 をクリックするか、Ctrl-F5 を押してください。
/**
* 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.');
// KJP Harness: audit public guidance pages
KJPHarness.auditPublicPages = function () {
var pages = [
{
title: 'Help:編集ガイド',
required: ['編集の基本方針', '出典', 'やってはいけない編集']
},
{
title: 'Help:出典ガイド',
required: ['信頼性Tier', 'Evidence', 'S — Primary / Official']
},
{
title: 'Project:方針',
required: ['基本原則', '存命人物に関する情報', 'サイト管理']
},
{
title: 'Project:画像利用方針',
required: ['基本原則', 'フォトカード画像', '権利者からの申立て']
},
{
title: 'KPOP JPWikiについて',
required: ['KPOP JPWikiが目指すもの', 'EXPLORE', 'COMMUNITY']
},
{
title: 'Project:プライバシーポリシー',
required: [
'収集する可能性のある情報',
'Cookie等について',
'広告について',
'第三者への提供'
]
},
{
title: 'Project:お問い合わせ',
required: [
'サイトに関する問い合わせ',
'著作権・画像・その他の権利に関する申立て',
'セキュリティ上の問題'
]
}
];
return Promise.all(
pages.map(function (page) {
return KJPHarness.getSource(page.title).then(function (source) {
var text = source && typeof source.text === 'string'
? source.text
: '';
var missing = page.required.filter(function (requiredText) {
return text.indexOf(requiredText) === -1;
});
return {
page: page.title,
exists: !!source && !!source.pageid,
length: text.length,
requiredSections: missing.length === 0 ? 'OK' : 'NG',
missing: missing.join(', ')
};
}).catch(function (error) {
return {
page: page.title,
exists: false,
length: 0,
requiredSections: 'ERROR',
missing: String(error)
};
});
})
).then(function (results) {
console.table(results);
return results;
});
};