// <nowiki>
/*
var _addText="{{工具警告|QuickCopy}}";
var _addText="{{Notice|<big>'''文檔請見:[[User:Addonian1123/QuickCopy]]'''</big>}}";
*/
$(() => {
mw.loader.using('mediawiki.util').then(() => {
const userLang = mw.config.get('wgUserLanguage') || mw.config.get('wgUserVariant') || 'zh-hant';
const isHans = ['zh-hans', 'zh-cn', 'zh-my', 'zh-sg'].some(l => userLang.toLowerCase().startsWith(l));
const skin = mw.config.get('skin');
let portletId = 'p-cactions';
if (!document.getElementById('p-cactions') || skin === 'minerva') {
portletId = 'p-tb';
}
const copyNode = mw.util.addPortletLink(
portletId,
'#',
isHans ? '复制页面' : '複製頁面',
'ca-quickcopy',
isHans ? '复制本页内容或从其他页面复制' : '複製本頁內容或從其他頁面複製'
);
if (!copyNode) {
return;
}
let windowManager;
$(copyNode).on('click', (e) => {
e.preventDefault();
mw.loader.using(['mediawiki.api', 'mediawiki.Title', 'oojs-ui-core', 'oojs-ui-windows']).then(() => {
openOOUIDialog();
});
});
function openOOUIDialog() {
if (!windowManager) {
const msg = {
title: isHans ? 'QuickCopy 复制页面' : 'QuickCopy 複製頁面',
direction_label: isHans ? '复制方向:' : '複製方向:',
direction_forward: isHans ? '从本页复制至目标页面' : '從本頁複製至目標頁面',
direction_reverse: isHans ? '从目标页面复制至本页' : '從目標頁面複製至本頁',
target_label_forward: isHans ? '目标页面(必填):' : '目標頁面(必填):',
target_label_reverse: isHans ? '来源页面(必填):' : '來源頁面(必填):',
target_placeholder_forward: isHans ? '批量请用 | 或换行分隔,例如:User:Example/沙盒1 | User:Example/沙盒2' : '批量請用 | 或換行分隔,例如:User:Example/沙盒1 | User:Example/沙盒2',
target_placeholder_reverse: isHans ? '例如:User:Example/沙盒' : '例如:User:Example/沙盒',
summary_label: isHans ? '编辑摘要(可选):' : '編輯摘要(可選):',
submit: isHans ? '发布编辑' : '發布編輯',
cancel: isHans ? '取消' : '取消',
empty_target: isHans ? '页面名称不能为空' : '頁面名稱不能為空',
copying: isHans ? '正在复制页面...' : '正在複製頁面...',
success: isHans ? '复制成功。' : '複製成功。',
error_get: isHans ? '无法获取页面内容' : '無法抓取頁面內容',
warn_mainspace: isHans ? '警告:您正在将草稿、用户页或沙盒的内容复制到主条目空间,请优先使用移动功能。' : '警告:您正在將草稿、用戶頁或沙盒的內容複製到主條目空間,請優先使用移動功能。',
summary_placeholder: isHans ? '输入编辑摘要' : '輸入編輯摘要',
};
function QuickCopyDialog(config) {
QuickCopyDialog.super.call(this, config);
}
OO.inheritClass(QuickCopyDialog, OO.ui.ProcessDialog);
QuickCopyDialog.static.name = 'quickcopy';
QuickCopyDialog.static.title = msg.title;
QuickCopyDialog.static.actions = [
{ action: 'save', label: msg.submit, flags: ['primary', 'progressive'] },
{ label: msg.cancel, flags: 'safe' }
];
QuickCopyDialog.prototype.initialize = function () {
QuickCopyDialog.super.prototype.initialize.apply(this, arguments);
this.directionSelect = new OO.ui.RadioSelectWidget({
items: [
new OO.ui.RadioOptionWidget({ data: 'forward', label: msg.direction_forward }),
new OO.ui.RadioOptionWidget({ data: 'reverse', label: msg.direction_reverse })
]
});
this.directionSelect.selectItemByData('forward');
this.targetInput = new OO.ui.MultilineTextInputWidget({
autosize: true,
rows: 2,
required: true,
placeholder: msg.target_placeholder_forward
});
this.targetField = new OO.ui.FieldLayout(this.targetInput, {
label: msg.target_label_forward,
align: 'top'
});
this.summaryInput = new OO.ui.TextInputWidget({
placeholder: msg.summary_placeholder
});
this.warningMessage = new OO.ui.MessageWidget({
type: 'warning',
inline: true,
label: msg.warn_mainspace
});
this.warningMessage.toggle(false);
this.panel = new OO.ui.PanelLayout({ padded: true, expanded: false });
this.fieldsetLayout = new OO.ui.FieldsetLayout();
this.fieldsetLayout.addItems([
new OO.ui.FieldLayout(this.directionSelect, { label: msg.direction_label, align: 'top' }),
this.targetField,
new OO.ui.FieldLayout(this.summaryInput, { label: msg.summary_label, align: 'top' })
]);
this.panel.$element.append(this.fieldsetLayout.$element, this.warningMessage.$element);
this.$body.append(this.panel.$element);
this.directionSelect.on('select', this.onInputChange.bind(this));
this.targetInput.on('change', this.onInputChange.bind(this));
};
QuickCopyDialog.prototype.onInputChange = function () {
const direction = this.directionSelect.findSelectedItem()?.getData() || 'forward';
const rawVal = this.targetInput.getValue();
const targets = rawVal.split(/[\n|,,、]/).map(s => s.trim()).filter(Boolean);
if (direction === 'forward') {
this.targetField.setLabel(msg.target_label_forward);
this.targetInput.$input.attr('placeholder', msg.target_placeholder_forward);
} else {
this.targetField.setLabel(msg.target_label_reverse);
this.targetInput.$input.attr('placeholder', msg.target_placeholder_reverse);
}
this.actions.setAbilities({ save: targets.length > 0 });
this.checkNamespaceWarning(direction, targets);
this.updateSize();
};
QuickCopyDialog.prototype.checkNamespaceWarning = function (direction, targets) {
if (targets.length === 0) {
this.warningMessage.toggle(false);
return;
}
const isDraftUserSandbox = (ns, title) => {
if (ns === 2 || ns === 118) return true;
const t = title.toLowerCase();
return t.includes('sandbox') || t.includes('沙盒');
};
let showWarning = false;
if (direction === 'forward') {
const sourceNs = mw.config.get('wgNamespaceNumber');
const sourceTitle = mw.config.get('wgTitle');
if (isDraftUserSandbox(sourceNs, sourceTitle)) {
showWarning = targets.some(t => {
const titleObj = mw.Title.newFromText(t);
return titleObj && titleObj.getNamespaceId() === 0;
});
}
} else {
const currentNs = mw.config.get('wgNamespaceNumber');
if (currentNs === 0) {
const sourceTitleObj = mw.Title.newFromText(targets[0]);
if (sourceTitleObj) {
showWarning = isDraftUserSandbox(
sourceTitleObj.getNamespaceId(),
sourceTitleObj.getMainText()
);
}
}
}
this.warningMessage.toggle(showWarning);
};
QuickCopyDialog.prototype.getSetupProcess = function (data) {
return QuickCopyDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.directionSelect.selectItemByData('forward');
this.targetInput.setValue('');
this.summaryInput.setValue('');
this.warningMessage.toggle(false);
this.actions.setAbilities({ save: false });
}, this);
};
QuickCopyDialog.prototype.getReadyProcess = function (data) {
return QuickCopyDialog.super.prototype.getReadyProcess.call(this, data)
.next(function () {
this.targetInput.focus();
}, this);
};
QuickCopyDialog.prototype.getActionProcess = function (action) {
const dialog = this;
if (action === 'save') {
const direction = this.directionSelect.findSelectedItem()?.getData() || 'forward';
const rawVal = this.targetInput.getValue();
const targets = rawVal.split(/[\n|,,、]/).map(s => s.trim()).filter(Boolean);
const userSummary = this.summaryInput.getValue().trim();
if (targets.length === 0) {
return new OO.ui.Process(function () {
return $.Deferred().reject(new OO.ui.Error(msg.empty_target)).promise();
});
}
return new OO.ui.Process(function () {
return executeCopy(targets, userSummary, direction, msg).then(function () {
dialog.close();
});
});
}
return QuickCopyDialog.super.prototype.getActionProcess.call(this, action);
};
windowManager = new OO.ui.WindowManager();
$('body').append(windowManager.$element);
const dialog = new QuickCopyDialog();
windowManager.addWindows([dialog]);
}
windowManager.openWindow('quickcopy');
}
function executeCopy(targets, userSummary, direction, msg) {
const api = new mw.Api();
const currentTitle = mw.config.get('wgPageName');
const suffix = '[[User:Addonian1123/QuickCopy|QuickCopy]]';
const finalSummary = userSummary ? `${userSummary} // ${suffix}` : suffix;
mw.notify(msg.copying);
if (direction === 'forward') {
return api.get({
action: 'query',
prop: 'revisions',
titles: currentTitle,
rvprop: 'content',
rvslots: 'main',
formatversion: 2
}).then((data) => {
const pages = data.query.pages;
if (!pages || !pages[0] || !pages[0].revisions) {
return $.Deferred().reject(new OO.ui.Error(msg.error_get));
}
const content = pages[0].revisions[0].slots.main.content;
const promises = targets.map(t => {
return api.postWithToken('csrf', {
action: 'edit',
title: t,
text: content,
summary: finalSummary
});
});
return $.when.apply($, promises);
}).then(() => {
mw.notify(msg.success, { type: 'success' });
setTimeout(() => {
window.location.href = mw.util.getUrl(targets[0]);
}, 1000);
return $.Deferred().resolve().promise();
}).catch((err) => {
const errMsg = (err instanceof OO.ui.Error) ? err : new OO.ui.Error(typeof err === 'string' ? err : JSON.stringify(err));
return $.Deferred().reject(errMsg).promise();
});
} else {
const sourceTitle = targets[0];
return api.get({
action: 'query',
prop: 'revisions',
titles: sourceTitle,
rvprop: 'content',
rvslots: 'main',
formatversion: 2
}).then((data) => {
const pages = data.query.pages;
if (!pages || !pages[0] || !pages[0].revisions) {
return $.Deferred().reject(new OO.ui.Error(msg.error_get));
}
const content = pages[0].revisions[0].slots.main.content;
return api.postWithToken('csrf', {
action: 'edit',
title: currentTitle,
text: content,
summary: finalSummary
});
}).then((result) => {
if (result && result.edit && result.edit.result === 'Success') {
mw.notify(msg.success, { type: 'success' });
setTimeout(() => {
window.location.reload();
}, 1000);
return $.Deferred().resolve().promise();
} else {
return $.Deferred().reject(new OO.ui.Error(JSON.stringify(result))).promise();
}
}).catch((err) => {
const errMsg = (err instanceof OO.ui.Error) ? err : new OO.ui.Error(typeof err === 'string' ? err : JSON.stringify(err));
return $.Deferred().reject(errMsg).promise();
});
}
}
});
});
// </nowiki>