User:Skmp/js/sampi tools.js
Appearance
(Redirected from User:Skmp/ϡ)
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)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/*
Please don't use this script - it can be buggy and is not meant to be used by other people.
*/
mw.loader.using('jquery.ui', function() {
const user_warnings_level_colors = {
"1": "#0000ff44",
"2": "#ffa50044",
"3": "#ff550044",
"4": "#ff000044",
"4im" : "#00000044"
}
const user_warnings_config = {
// Vandalism
"uw-vandalism": {
text: "⛔ Blatant vandalism",
short_name: "Vandalism",
options: ["1","2","3","4","4im"],
background_color: "#ff000044"
},
"uw-subtle": {
text: "❗❗ Subtle vandalism",
short_name: "Vandalism",
options: ["1","2","3","4"],
background_color: "#ff000044"
},
"uw-disruptive": {
text: "💥 Disruptive editing",
short_name: "Disruptive editing",
options: ["1","2","3",":uw-generic4"],
background_color: "#ff000044"
},
// "Soft" vandalism
"uw-joke": {
text: "😜 Improper humor in articles",
short_name: "Improper humor in articles",
options: ["1","2","3","4", "4im"],
background_color: "#ff550044"
},
// Destructive, but not necessarily vandalism
"uw-delete": {
text: "🪓 Removal of content without adequate explanation",
short_name: "Removal of content",
options: ["1","2","3","4","4im"],
background_color: "#ff550044"
},
// Spam
"uw-advert": {
text: "📰 Using Wikipedia for advertising or promotion",
short_name: "Advertising",
options: ["1","2","3","4","4im"],
background_color: "#ff550044"
},
"uw-spam": {
text: "🔗 Adding spam links",
short_name: "Spam",
options: ["1","2","3","4","4im"],
background_color: "#ff550044"
},
// BIO
"uw-biog": {
text: "🚫 Adding unreferenced controversial information about living persons",
short_name: "Adding unreferenced controversial information",
options: ["1","2","3","4", "4im"],
background_color: "#ffa50044"
},
// Inexperienced users
"uw-npov": {
text: "🛑 Not adhering to neutral point of view",
short_name: "Neutral point of view",
options: ["1","2","3","4"],
background_color: "#ffa50044"
},
"uw-test": {
text: "🧪 Editing tests",
short_name: "Test edits",
options: ["1","2","3",":uw-vandalism4"],
background_color: "#0000ff44"
},
"uw-talkinarticle": {
text: "💬 Talking in articles",
short_name: "Talking in articles",
options: ["1","2","3",":uw-generic4"],
background_color: "#0000ff44"
},
}
const dialog_gsr = $(`\
<div id="sampi-dialog-gsr">
<fieldset style="display: flex; flex-direction: column; margin-top: 0" name="main-info">
<legend>Request info</legend>
<div><label for="request-text">Request text</label></div>
<input class="request-text" name="request-text">
<div>Common reasons: \
<a href="javascript:sampi_set_gsr_text('requesting page deletion')">delete page</a>;
<a href="javascript:sampi_set_gsr_text('requesting user/ip block - vandalism')">vandalism</a>
</div>
</fieldset>
<pre class="final-request-text"></pre>
</div>
`)
.dialog({
title: "New GS request",
closeOnEscape: true,
autoOpen: false,
resizable: false,
width: 600,
dialogClass: "sampi-gsr-dialog-root",
buttons: [{
text: "Send",
click: function() {
gsr_send();
}
}]
});
const dialog_uw = $(`\
<div id="sampi-dialog-uw">
<fieldset style="display: flex; flex-direction: column; margin-top: 0" name="main-info">
<legend>Warning info</legend>
<div><label for="warning-type">Warning type</label></div>
<select class="warning-type" name="warning-type"></select>
<div style="margin-top: 0.5em"><label for="warning-level">Warning level</label></div>
<select class="warning-level" name="warning-level"></select>
<div style="margin-top: 0.5em"><label for="additional-text">Additional text</label></div>
<input class="additional-text" name="additional-text">
<div>Common: \
<a href="javascript:sampi_set_uw_text('#offer_help')">offer help</a>
</div>
</fieldset>
<pre class="final-warning-wikitext"></pre>
</div>
`)
.dialog({
title: "Leave user a warning",
closeOnEscape: true,
autoOpen: false,
resizable: false,
width: 600,
dialogClass: "sampi-uw-dialog-root",
buttons: [{
text: "Send",
click: function() {
uw_send();
}
}]
});
let inject_toolbox = function (scopes) {
const toolbox_menu_html = `\
<nav class="mw-portlet vector-menu vector-menu-dropdown vector-menu-dropdown-noicon vectorMenu"><h3><span>ϡ</span></h3><div class="vector-menu-content"><ul class="vector-menu-content-list">
${ scopes.includes("user") ? `\
<li><a id="sampi-toolbox-button-GSR" href="javascript:sampi_toolbox_gsr_click()"><span><s>GSR (WIP)</s></span></a></li>
<li><a id="sampi-toolbox-button-UW" href="javascript:sampi_toolbox_uw_click()"><span>Warn user</span></a></li>
` : "" }
</ul></div></nav>
`;
$('#p-search').before(toolbox_menu_html);
}
window.sampi_toolbox_gsr_click = function () {
const final_request_text = $("#sampi-dialog-gsr > .final-request-text");
final_request_text.text(`* ${ window.location.href } ~~` + "~~");
document.querySelector("#sampi-dialog-gsr .request-text").oninput = function (e) {
final_request_text.text(`* ${ window.location.href }${ e.target.value ? " " + e.target.value : "" } ~~` + "~~");
};
$("#sampi-dialog-gsr").dialog("open");
}
window.sampi_toolbox_uw_click = function () {
const button = document.querySelector(".sampi-uw-dialog-root button");
const button_text = button.getElementsByTagName("span")[0];
const final_warning_wikitext = document.querySelector("#sampi-dialog-uw .final-warning-wikitext");
const warning_type_selector = document.querySelector("#sampi-dialog-uw .warning-type");
const warning_level_selector = document.querySelector("#sampi-dialog-uw .warning-level");
const additional_text_input = document.querySelector("#sampi-dialog-uw .additional-text");
for(const warning_type in user_warnings_config) {
// TODO @performance this is not very efficient
warning_type_selector.innerHTML += `<option value="${ warning_type }" style="background-color: ${ user_warnings_config[warning_type].background_color }">${ user_warnings_config[warning_type].text }</option>`
}
warning_type_selector.onchange = function (e) {
warning_level_selector.innerHTML = "";
const selected_type = e.target.value;
if(!user_warnings_config[selected_type]) return;
for(const warning_level of user_warnings_config[selected_type].options) {
// TODO @performance this is not very efficient
warning_level_selector.innerHTML += `<option value="${ warning_level }" style="background-color: ${ user_warnings_level_colors[warning_level] || "unset" }">${ warning_level[0] === ":" ? `{{${ warning_level.substring(1) }}}` : warning_level }</option>`
}
warning_level_selector.value = "-";
final_warning_wikitext.innerText = "";
}
warning_level_selector.onchange = function (e) {
if(warning_level_selector.value[0] === ":") {
final_warning_wikitext.dataset.template = warning_level_selector.value.substring(1);
final_warning_wikitext.dataset.short_name = "";
} else {
final_warning_wikitext.dataset.template = warning_type_selector.value + warning_level_selector.value;
final_warning_wikitext.dataset.short_name = user_warnings_config[warning_type_selector.value].short_name;
}
final_warning_wikitext.innerText = "{{sub" + `st:${ final_warning_wikitext.dataset.template }}}${ additional_text_input.value ? `
:${ additional_text_input.value }` : "" } ~~` + `~~`;
button_text.innerText = "Send";
button.style.pointerEvents = "all";
}
additional_text_input.oninput = function (e) {
if(!final_warning_wikitext.dataset.template) return;
final_warning_wikitext.innerText = "{{sub" + `st:${ final_warning_wikitext.dataset.template }}}${ additional_text_input.value ? `
:${ additional_text_input.value }` : "" } ~~` + `~~`;
};
warning_type_selector.value = "-";
$("#sampi-dialog-uw").dialog("open");
}
window.sampi_set_gsr_text = function (text) {
const request_text_input = document.querySelector("#sampi-dialog-gsr .request-text");
request_text_input.value = text;
request_text_input.dispatchEvent(new Event('input', { bubbles: true, value: text }));
}
window.sampi_set_uw_text = function (text) {
const additional_text_input = document.querySelector("#sampi-dialog-uw .additional-text");
let final_text = text;
if(text === "#offer_help") {
final_text = `If you have questions or need help, feel free to leave me a message on this page, or on [[${ mw.config.get("wgFormattedNamespaces")[3] }:${ mw.config.get("wgUserName") }|my talk page]].`;
}
additional_text_input.value = final_text;
additional_text_input.dispatchEvent(new Event('input', { bubbles: true, value: final_text }));
}
let get_csrf_token = function (cb) {
fetch(`/w/api.php?action=query&format=json&meta=tokens`, {
method: "GET"
})
.then(response => {
response.json()
.then(json => {
if(json && json.query && json.query.tokens && json.query.tokens.csrftoken) {
cb(json.query.tokens.csrftoken);
}
})
});
}
const gsr_send = function() {
const button = document.querySelector(".sampi-gsr-dialog-root button");
const button_text = button.getElementsByTagName("span")[0];
button_text.innerText = "Sending...";
button.style.pointerEvents = "none";
// Send the request
const api = new mw.ForeignApi("https://meta.wikimedia.org/w/api.php");
api.postWithToken("csrf", {
action: "edit",
title: "User:Skmp/Sandbox",
summary: "Requesting action from global sysops",
appendtext: `
${ $("#sampi-dialog-gsr > .final-request-text").text() }`,
nocreate: 1,
})
.then(json => {
if(json.edit && json.edit.result === "Success") {
button_text.innerText = "Sent!";
} else {
if(json.error) alert(json.error.info);
button_text.innerText = "Error!";
console.error("[Sampi error]", json.error);
}
});
}
const uw_send = function() {
const final_warning_wikitext = document.querySelector("#sampi-dialog-uw .final-warning-wikitext");
const button = document.querySelector(".sampi-uw-dialog-root button");
const button_text = button.getElementsByTagName("span")[0];
if(!final_warning_wikitext.dataset.template) {
button_text.innerText = "Please, select a template";
return;
}
button_text.innerText = "Checking the template...";
button.style.pointerEvents = "none";
const api = new mw.Api();
// Check if selected template exists on this wiki
api.get({
action: "query",
titles: `Template:${ final_warning_wikitext.dataset.template }`,
})
.then(json => {
if(json.query.pages["-1"]) {
button_text.innerText = "This wiki does not have such template";
return;
}
button_text.innerText = "Saving the talk page...";
// TODO @cleanup @refactor this isn't the best approach
const date_str = new Date().toGMTString().split(" ", 4).join(" ");
const section_title = `${ date_str }${ final_warning_wikitext.dataset.short_name ? `: ${ final_warning_wikitext.dataset.short_name }` : "" }`;
const watch_date = new Date();
watch_date.setDate(watch_date.getDate() + 5);
api.postWithToken('csrf', {
action: "edit",
title: `${ mw.config.get("wgFormattedNamespaces")[3] }:${ mw.config.get("wgRelevantUserName") }`,
section: "new",
sectiontitle: section_title,
summary: `+${ final_warning_wikitext.dataset.template }; Leaving user a warning ([[:meta:User:Skmp/ϡ|ϡ]])`,
appendtext: final_warning_wikitext.innerText,
watchlist: "watch",
watchlistexpiry: watch_date.toISOString()
})
.then(json => {
if(json.edit && json.edit.result === "Success") {
button_text.innerText = "Sent!";
// If the "talk" link is red - make it blue, we have just created a talk page for a user
const talk_link_el = document.querySelector(".mw-changeslist-links > span > a");
if(talk_link_el.classList.contains("new")) {
const url = new URLSearchParams(talk_link_el.href);
url.delete("action");
url.delete("redlink");
talk_link_el.href = decodeURIComponent(url.toString());
talk_link_el.className = "";
talk_link_el.style.backgroundColor = "#ffd700";
}
} else {
if(json.error) alert(json.error.info);
button_text.innerText = "Error!";
console.error("[Sampi error]", json.error);
}
});
});
}
let append_revision_links = function (pagehistory_el, is_page_history) {
const revision_els = pagehistory_el.children;
// Go through all the revisions on the page
for(const revision_el of revision_els) {
const sampi_el = document.createElement("span");
sampi_el.className = "mw-changeslist-links";
sampi_el.style = "border: 1px dashed #00bfff; background-color: rgb(0 191 255 / 15%); padding: 0 0.2em; margin-left: 0.2em";
const sampi_text_el = document.createElement("span");
sampi_text_el.innerHTML = "ϡ";
sampi_text_el.style = "font-family: monospace";
const sampi_undo_btn_el = document.createElement("div");
sampi_undo_btn_el.className = "sampi-action-button";
sampi_undo_btn_el.style = "color: #0645ad; cursor: pointer; display: inline-block; margin-left: 0.25em";
sampi_undo_btn_el.innerHTML = "⤺ undo";
sampi_undo_btn_el.title = "Undo this revision";
sampi_el.appendChild(sampi_text_el);
sampi_el.appendChild(sampi_undo_btn_el);
// Undo revision click
sampi_undo_btn_el.addEventListener("click", e => {
// Require a summary to perform the undo
let custom_reason = prompt();
if(!custom_reason) {
alert("Action was not performed");
return;
};
sampi_undo_btn_el.style.pointerEvents = "none";
sampi_undo_btn_el.style.color = "gray";
sampi_undo_btn_el.innerText = "1/3";
let target_revid_author_username;
let target_page_id;
const target_revid = revision_el.dataset.mwRevid;
// Get info about the target revision
fetch(`/w/api.php?action=query&format=json&prop=revisions&revids=${ target_revid }&rvprop=user`, {
method: "GET"
})
.then(response => {
response.json()
.then(selected_rev_json => {
sampi_undo_btn_el.innerText = "2/3";
if(selected_rev_json && selected_rev_json.query && selected_rev_json.query.pages) {
for(const selected_rev_page_id in selected_rev_json.query.pages) {
target_revid_author_username = selected_rev_json.query.pages[selected_rev_page_id].revisions[0].user;
target_page_id = selected_rev_json.query.pages[selected_rev_page_id].pageid;
}
}
if(!target_revid_author_username || !target_page_id) {
alert("Undo error. Could not get the author of the target revision or the target page");
return;
}
get_csrf_token(function (csrf_token) {
sampi_undo_btn_el.innerText = "3/3";
// Perform the undo
fetch(`/w/api.php?action=edit&format=json`, {
method: "POST",
body: new URLSearchParams({
pageid: target_page_id,
minor: 1,
nocreate: 1,
token: csrf_token,
undo: target_revid,
summary: `Undid revision ${ target_revid } by [[Special:Contributions/${ target_revid_author_username }|${ target_revid_author_username }]]; ${ custom_reason } ([[:meta:User:Skmp/ϡ|ϡ]])`,
})
})
.then(response => {
response.json()
.then(undo_json => {
if(undo_json.edit && undo_json.edit.result === "Success") {
sampi_undo_btn_el.style.color = "green";
sampi_undo_btn_el.style.fontWeight = "bold";
sampi_undo_btn_el.innerText = "done";
} else {
sampi_undo_btn_el.style.color = "crimson";
sampi_undo_btn_el.style.fontWeight = "bold";
sampi_undo_btn_el.innerText = undo_json.error ? undo_json.error.info : "error!";
console.error("[Sampi error]", undo_json.edit);
}
})
});
});
});
});
});
// Revert to this revision
if(is_page_history) {
const sampi_restore_btn_el = document.createElement("div");
sampi_restore_btn_el.className = "sampi-action-button";
sampi_restore_btn_el.style = "color: #0645ad; cursor: pointer; display: inline-block; margin-left: 0.25em";
sampi_restore_btn_el.innerHTML = "↢ restore";
sampi_restore_btn_el.title = "Restore page to this revision";
sampi_el.appendChild(sampi_restore_btn_el);
sampi_restore_btn_el.addEventListener("click", e => {
let custom_reason = false;
// ctrl down - skip summary
if(!e.ctrlKey) {
custom_reason = prompt();
if(!custom_reason) {
alert("Action was not performed");
return;
}
}
sampi_restore_btn_el.style.pointerEvents = "none";
sampi_restore_btn_el.style.color = "gray";
sampi_restore_btn_el.innerText = "1/4";
let newest_revid;
let target_revid_author_username;
let target_page_id;
const target_revid = revision_el.dataset.mwRevid;
// Get info about the selected revision
fetch(`/w/api.php?action=query&format=json&prop=revisions&revids=${ target_revid }&rvprop=user`, {
method: "GET"
})
.then(response => {
response.json()
.then(selected_rev_json => {
sampi_restore_btn_el.innerText = "2/4";
if(selected_rev_json && selected_rev_json.query && selected_rev_json.query.pages) {
for(const selected_rev_page_id in selected_rev_json.query.pages) {
target_revid_author_username = selected_rev_json.query.pages[selected_rev_page_id].revisions[0].user;
target_page_id = selected_rev_json.query.pages[selected_rev_page_id].pageid;
}
}
if(!target_revid_author_username) {
alert("Restore error. Could not get the author of the target revision");
return;
}
// Get the last revision of this page
fetch(`/w/api.php?action=query&format=json&prop=revisions&pageids=${ target_page_id }&rvlimit=1&rvdir=older`, {
method: "GET"
})
.then(response => {
response.json()
.then(last_rev_json => {
sampi_restore_btn_el.innerText = "3/4";
if(last_rev_json && last_rev_json.query && last_rev_json.query.pages) {
for(const last_rev_page_id in last_rev_json.query.pages) {
newest_revid = last_rev_json.query.pages[last_rev_page_id].revisions[0].revid;
if(!newest_revid || !target_revid || !target_page_id) {
alert("Error reverting to a revision. Some id is not known!");
return;
}
// Get a csrf token
get_csrf_token(function (csrf_token) {
sampi_restore_btn_el.innerText = "4/4";
// Perform the revert
fetch(`/w/api.php?action=edit&format=json`, {
method: "POST",
body: new URLSearchParams({
pageid: target_page_id,
minor: 1,
nocreate: 1,
summary: `Reverted page to revision ${ target_revid } by [[Special:Contributions/${ target_revid_author_username }|${ target_revid_author_username }]]${ custom_reason ? `; ${ custom_reason }` : "" } ([[:meta:User:Skmp/ϡ|ϡ]])`,
token: csrf_token,
undo: newest_revid,
undoafter: target_revid
})
})
.then(response => {
response.json()
.then(revert_json => {
if(revert_json.edit && revert_json.edit.result === "Success") {
sampi_restore_btn_el.style.color = "green";
sampi_restore_btn_el.style.fontWeight = "bold";
sampi_restore_btn_el.innerText = "done, reloading...";
window.location.reload();
} else {
sampi_restore_btn_el.style.color = "crimson";
sampi_restore_btn_el.style.fontWeight = "bold";
sampi_restore_btn_el.innerText = revert_json.error ? revert_json.error.info : "error!";;
console.error("[Sampi error]", revert_json.edit);
}
})
});
})
}
}
})
});
})
});
return false;
});
}
// Check if this revision can be rolled back
const rollback_link = revision_el.querySelector(".mw-rollback-link > a");
if(rollback_link) {
// Add rollback links
const sampi_rollback_btn_el = document.createElement("div");
sampi_rollback_btn_el.className = "sampi-action-button";
sampi_rollback_btn_el.style = "color: #0645ad; cursor: pointer; display: inline-block; margin-left: 0.25em";
sampi_rollback_btn_el.innerHTML = "⤫ rollback";
sampi_rollback_btn_el.title = "Rollback edits by this user";
sampi_el.appendChild(sampi_rollback_btn_el);
// On rollback link click
sampi_rollback_btn_el.addEventListener("click", e => {
// Require ctrl key to perform a rollback
if(!e.ctrlKey) {
sampi_rollback_btn_el.innerHTML += " (hold ctrl!)";
return;
}
sampi_rollback_btn_el.style.pointerEvents = "none";
sampi_rollback_btn_el.style.color = "gray";
sampi_rollback_btn_el.innerText = "...";
// Get the revision info from the mw's rollback link
const rollback_link_split = rollback_link.href.split("?", 2);
const rollback_link_params = new URLSearchParams(rollback_link_split[1]);
// Get the url params from the link
const token = rollback_link_params.get("token");
const title = rollback_link_params.get("title");
const user = rollback_link_params.get("from");
if(!token || !title || !user) {
alert("Rollback error. Could not get all the necessary information!");
} else {
// Perform a rollback
fetch(`/w/api.php?action=rollback&format=json`, {
method: "POST",
body: new URLSearchParams({
title,
user,
token
})
})
.then(response => {
response.json()
.then(json => {
if(json && json.rollback && json.rollback.revid) {
sampi_rollback_btn_el.style.color = "green";
sampi_rollback_btn_el.style.fontWeight = "bold";
sampi_rollback_btn_el.innerText = "done";
}
})
});
}
return false;
});
}
revision_el.appendChild(sampi_el);
}
}
let append_useful_contribs_links = function() {
const tools_el = document.getElementsByClassName("mw-contributions-user-tools")[0];
const username = mw.config.get('wgPageName').split("/", 2)[1];
tools_el.innerHTML += ` \
<span class="mw-changeslist-links">
<span>ϡ</span>
<span><a target="_blank" href="https://guc.toolforge.org/?user=${ username }&blocks=true">GUC</a></span>
</span>`
}
// Script start
let pagehistory_el = document.getElementById("pagehistory");
let contributionslist_el = document.getElementsByClassName("mw-contributions-list")[0];
const is_contribs_page = mw.config.get("wgCanonicalSpecialPageName") === "Contributions";
const is_user_talk_page = mw.config.get("wgCanonicalNamespace") === "User_talk";
let toolbox_scopes = [];
// TODO @cleanup @refator use mw.config.get('wgCanonicalSpecialPageName'), mw.config.get('wgNamespaceNumber'), etc. instead
// If this page has a history or contributions in it - append revision-related links
if(pagehistory_el || contributionslist_el) {
append_revision_links(pagehistory_el || contributionslist_el, pagehistory_el && true);
}
if(is_contribs_page) {
// Contribs page
append_useful_contribs_links();
toolbox_scopes = toolbox_scopes.concat(["user", "contribs"]);
}
if(is_user_talk_page) {
toolbox_scopes.push("user");
}
inject_toolbox(toolbox_scopes);
});