MediaWiki:Gadget-Voting.js
/**
* GAWAI PEMILIHAN / VOTING
*
* ⚠️ Harap diperhatikan jika Anda mengubah skrip ⚠️
* ini maka Anda akan memengaruhi seluruh pengguna
* Wikipedia bahasa Indonesia
*
* Untuk mengatur konfigurasi skrip ini
* (contoh halaman, periode, dan syarat minimal)
* silahkan kunjungi [[MediaWiki:VotingConfig.json]]
*
* Pemelihara: [[Pengguna:Janorovic Volkov]]
*/
// <nowiki>
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows']).then(function(){
const api = new mw.Api();
const page = mw.config.get("wgPageName");
const user = mw.config.get("wgUserName");
const action = mw.config.get("wgAction");
if(action !== "view") return;
if(!user) return;
const windowManager = new OO.ui.WindowManager();
$(document.body).append(windowManager.$element);
async function loadConfig(){
const res = await api.get({
action:"query",
prop:"revisions",
titles:"MediaWiki:VotingConfig.json",
rvprop:"content",
rvslots:"main"
});
const obj = Object.values(res.query.pages)[0];
return JSON.parse(obj.revisions[0].slots.main["*"]);
}
// =========================
// [UPDATE] ambil config per halaman
// =========================
function getVoteConfig(cfg){
return cfg.votes.find(v => v.page === page);
}
function isWithinPeriod(cfg){
const now = new Date();
const start = new Date(cfg.startDate+"T00:00:00Z");
const end = new Date(cfg.endDate+"T23:59:59Z");
return now>=start && now<=end;
}
async function validateUser(cfg){
const res = await api.get({
action:"query",
meta:"userinfo",
uiprop:"editcount|registrationdate"
});
const info = res.query.userinfo;
let ageDays = 9999;
if(info.registrationdate){
const reg = new Date(info.registrationdate);
ageDays = (new Date()-reg)/86400000;
}
return (
info.editcount >= cfg.userContribMin &&
ageDays >= cfg.userAgeMinDays
);
}
async function findSection(sectionName){
const res = await api.get({
action:"parse",
page:page,
prop:"sections"
});
const sections = res.parse.sections;
for(const s of sections){
if(s.line.toLowerCase() === sectionName.toLowerCase()){
return s.index;
}
}
return null;
}
async function alreadyVoted(){
const sectionIndex = await findSection("Pemungutan suara");
if(sectionIndex === null) return false;
const res = await api.get({
action: "parse",
page: page,
prop: "wikitext",
section: sectionIndex
});
const text = res.parse.wikitext["*"];
const patterns = [
"[[User:"+user,
"[[Pengguna:"+user,
"[[user:"+user,
"[[pengguna:"+user
];
return patterns.some(p => text.includes(p));
}
function placeButtonAtSection(sectionId, wrap){
const target = document.getElementById(sectionId);
if(target){
$(target).parent().before(wrap);
return true;
}
return false;
}
function createButton(){
const btn = new OO.ui.ButtonWidget({
label:"🗳️ Berikan suara",
flags:["primary","progressive"]
});
const wrap = $("<div>")
.attr("id","votingButton")
.css({
textAlign:"center",
margin:"20px 0"
})
.append(btn.$element);
// =========================
// [UPDATE] taruh di atas header "Pemungutan suara"
// =========================
if(!placeButtonAtSection("Pemungutan_suara", wrap)){
$(".mw-body-content").prepend(wrap);
}
btn.on("click",openDialog);
}
function openDialog(){
function Dialog(config){
Dialog.super.call(this,config);
}
OO.inheritClass(Dialog,OO.ui.ProcessDialog);
Dialog.static.name = "voteDialog";
Dialog.static.title = "Berikan suara";
Dialog.static.size = "medium";
Dialog.static.actions = [
{action:"submit",label:"Kirim",flags:["primary","progressive"]},
{action:"cancel",label:"Batal",flags:"safe"}
];
Dialog.prototype.initialize = function (){
Dialog.super.prototype.initialize.call(this);
this.vote = new OO.ui.RadioSelectWidget({
items:[
new OO.ui.RadioOptionWidget({data:"support",label:"Setuju"}),
new OO.ui.RadioOptionWidget({data:"oppose",label:"Tidak setuju"}),
new OO.ui.RadioOptionWidget({data:"abstain",label:"Abstain"})
]
});
this.vote.selectItemByData("support");
this.comment = new OO.ui.MultilineTextInputWidget({
placeholder:"Komentar (opsional)",
rows:3
});
this.panel = new OO.ui.PanelLayout({
padded:true,
expanded:false
});
this.panel.$element.append(
this.vote.$element,
$("<br>"),
this.comment.$element
);
this.$body.append(this.panel.$element);
};
Dialog.prototype.getActionProcess = function(action){
if(action==="cancel"){
return new OO.ui.Process(()=>this.close());
}
if(action==="submit"){
return new OO.ui.Process(async ()=>{ // [UPDATE] async biar bisa await
const selected = this.vote.findSelectedItem();
const vote = selected ? selected.getData() : null;
const comment = this.comment.getValue();
if(!vote){
mw.notify("Pilih opsi terlebih dahulu");
return;
}
// =========================
// [UPDATE] cek ulang sebelum submit (anti race condition)
// =========================
if(await alreadyVoted()){
mw.notify("⚠️ Anda sudah memberikan suara");
return;
}
await submitVote(vote,comment); // [UPDATE] await
this.close();
$("#votingButton").html(
$("<b>").text("🗳️ Anda sudah memberikan suara!")
);
});
}
return Dialog.super.prototype.getActionProcess.call(this,action);
};
const dialog = new Dialog();
windowManager.addWindows([dialog]);
windowManager.openWindow(dialog);
}
async function submitVote(type,comment){
let template;
let sectionName;
if(type==="support"){
template="{{Setuju}}";
sectionName="Setuju";
}
if(type==="oppose"){
template="{{Tidak setuju}}";
sectionName="Tidak setuju";
}
if(type==="abstain"){
template="{{Abstain}}";
sectionName="Abstain";
}
const sectionIndex = await findSection(sectionName);
if(sectionIndex===null){
mw.notify("Header pemilihan tidak ditemukan");
return;
}
let voteText="\n# "+template+". ";
if(comment){
voteText+=comment+" ";
}
voteText+="~~~~\n";
await api.postWithToken("csrf",{
action:"edit",
title:page,
section:sectionIndex,
appendtext:voteText,
summary:"Memberikan suara ("+sectionName+")",
minor:true
});
}
(async function(){
const cfg = await loadConfig();
// =========================
// [UPDATE] ambil config khusus halaman ini
// =========================
const voteCfg = getVoteConfig(cfg);
if(!voteCfg) return;
// =========================
// [UPDATE] cek periode per halaman
// =========================
if(!isWithinPeriod(voteCfg)){
const now = new Date();
const start = new Date(voteCfg.startDate);
let text = "🗳️ Periode pemilihan telah berakhir";
if(now < start){
text = "🗳️ Pemungutan suara belum dimulai";
}
$(".mw-body-content").prepend(
$("<div>")
.css({textAlign:"center",margin:"20px",fontWeight:"bold"})
.text(text)
);
return;
}
if(!(await validateUser(cfg))) return;
if(await alreadyVoted()){
$(".mw-body-content").prepend(
$("<div>")
.css({textAlign:"center",margin:"20px",fontWeight:"bold"})
.text("🗳️ Anda sudah memberikan suara")
);
return;
}
createButton();
})();
});
// </nowiki>
Konten ini disalin dari wikipedia, mohon digunakan dengan bijak.


