1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-09-09 18:16:22 -07:00
gochan/frontend/ts/management/manage.ts

324 lines
No EOL
7.9 KiB
TypeScript
Executable file

import $ from "jquery";
import { alertLightbox } from "../dom/lightbox";
import { $topbar, TopBarButton } from "../dom/topbar";
import "./sections";
import "./filebans";
import "./viewlog";
import { isThreadLocked } from "../api/management";
const reportsTextRE = /^Reports( \(\d+\))?/;
export let staffActions: StaffAction[] = [];
let staffInfo: StaffInfo = null;
/**
* The menu shown when the Staff button on the top bar is clicked
*/
let $staffMenu: JQuery<HTMLElement> = null;
/**
* A button that opens $staffMenu
*/
let $staffBtn: TopBarButton = null;
function dropdownHasItem(dropdown: any, item: string) {
return [...dropdown.children].filter(v => v.text === item).length > 0;
}
function addManageEvents(_i: number, el: HTMLSelectElement) {
if(staffInfo === null || staffInfo.rank < 2) return;
const $el = $(el);
const $post = $(el.parentElement);
const isLocked = isThreadLocked($post);
if(!dropdownHasItem(el, "Staff Actions")) {
$el.append('<option disabled="disabled">Staff Actions</option>');
}
if(staffInfo.rank === 3 && $post.hasClass("op-post")) {
if(isLocked) {
$el.append("<option>Unlock thread</option>");
} else {
$el.append("<option>Lock thread</option>");
}
}
if(staffInfo.rank >= 2) {
if(!dropdownHasItem(el, "Posts from this IP")) {
$el.append("<option>Posts from this IP</option>");
}
if(!dropdownHasItem(el, "Ban IP address")) {
$el.append("<option>Ban IP address</option>");
}
}
if(!dropdownHasItem(el, "Filter similar posts")) {
$el.append("<option>Filter similar posts</option>");
}
}
function setupManagementEvents() {
if(staffInfo === null || !staffInfo.actions) return;
$<HTMLSelectElement>("select.post-actions").each(addManageEvents);
$(document).on("postDropdownAdded", function(_e, data) {
if(!data.dropdown) return;
data.dropdown.append("<option>Posts from this IP</option>");
data.dropdown.append("<option>Ban IP address</option>");
});
}
interface BanFileJSON {
bantype: string;
board?: string;
fingerprinter?: string;
json: number;
staffnote: string;
ban?: string;
banmsg?: string;
filename?: string;
dofilenameban?: string;
checksum?: string;
dochecksumban?: string;
}
export function banFile(banType: string, filename: string, checksum: string, staffNote = "") {
const xhrFields: BanFileJSON = {
bantype: banType,
staffnote: staffNote,
json: 1
};
switch(banType) {
case "filename":
xhrFields.filename = filename;
xhrFields.dofilenameban = "Create";
break;
case "checksum":
xhrFields.checksum = checksum;
xhrFields.dochecksumban = "Create";
break;
default:
break;
}
return $.post({
url: `${webroot}manage/filebans`,
data: xhrFields
});
}
export function banFileFingerprint(fingerprint: string, ipBan: boolean, reason?: string, staffNote?: string) {
const xhrFields: BanFileJSON = {
bantype: "checksum",
checksum: fingerprint,
fingerprinter: "ahash",
ban: ipBan?"on":"",
banmsg: reason,
staffnote: staffNote,
json: 1,
dochecksumban: "Create"
};
return $.post({
url: `${webroot}manage/filebans`,
data: xhrFields
});
}
export async function initStaff() {
if(staffInfo !== null || staffActions?.length > 0)
// don't make multiple unnecessary AJAX requests
return staffInfo;
return $.ajax({
method: "GET",
url: `${webroot}manage/staffinfo`,
async: true,
cache: false,
dataType: "json",
success: (result:string|StaffInfo) => {
if(typeof result === "string") {
try {
staffInfo = JSON.parse(result);
} catch(e) {
// presumably not logged in
staffActions = [];
}
} else if(typeof result === "object") {
staffInfo = result;
}
staffActions = staffInfo?.actions ?? [];
return staffInfo;
},
error: (e: JQuery.jqXHR) => {
console.error("Error getting actions list:", e);
}
}).then(() => {
if(staffInfo.rank > 0)
setupManagementEvents();
return staffInfo;
});
}
export async function getPostInfo(id: number):Promise<PostInfo> {
return $.ajax({
method: "GET",
url: `${webroot}manage/postinfo`,
data: {
postid: id
},
async: true,
cache: true,
dataType: "json"
});
}
export async function isLoggedIn() {
return await initStaff().then(info => {
return info.rank > 0;
});
}
export function banSelectedPost() {
const boardDirArr = location.pathname.split("/");
if(boardDirArr.length < 2) return;
const boardDir = boardDirArr[1];
const checks = $("input[type=checkbox]");
if(checks.length === 0) {
alertLightbox("No posts selected");
return false;
}
let postID = 0;
for(let i = 0; i < checks.length; i++) {
if(checks[i].id.indexOf("check") === 0) {
postID = Number.parseInt(checks[i].id.replace("check", ""));
break;
}
}
window.location.pathname = `${webroot}manage/bans?dir=${boardDir}&postid=${postID}`;
}
/**
* A helper function for creating a menu item
*/
function menuItem(action: StaffAction|string, isCategory = false) {
return isCategory ? $("<div/>").append($("<b/>").text(action as string)) : $("<div/>").append(
$("<a/>").prop({
href: `${webroot}manage/${(action as StaffAction).id}`
}).text((action as StaffAction).title)
);
}
function getAction(id: string) {
for(const action of staffActions) {
if(action.id === id) {
return action;
}
}
}
function filterAction(action: StaffAction, perms: number) {
return action.title !== "Logout"
&& action.title !== "Dashboard"
&& action.jsonOutput < 2
&& action.perms === perms;
}
/**
* Creates a list of staff actions accessible to the user if they are logged in.
* It is shown when the user clicks the Staff button
* @param staff an object representing the staff's username and rank
*/
export function createStaffMenu(staff = staffInfo) {
const rank = staff.rank;
if(rank === 0) return;
$staffMenu = $("<div/>").prop({
id: "staffmenu",
class: "dropdown-menu"
});
$staffMenu.append(
menuItem(getAction("logout")),
menuItem(getAction("dashboard")));
const janitorActions = staffActions.filter(val => filterAction(val, 1));
$staffMenu.append(menuItem("Janitorial", true));
for(const action of janitorActions) {
$staffMenu.append(menuItem(action));
}
if(rank >= 2) {
const modActions = staffActions.filter(val => filterAction(val, 2));
if(modActions.length > 0)
$staffMenu.append(menuItem("Moderation", true));
for(const action of modActions) {
$staffMenu.append(menuItem(action));
}
getReports().then(updateReports);
}
if(rank === 3) {
const adminActions = staffActions.filter(val => filterAction(val, 3));
if(adminActions.length > 0)
$staffMenu.append(menuItem("Administration", true));
for(const action of adminActions) {
$staffMenu.append(menuItem(action));
}
}
createStaffButton();
}
export function addStaffThreadOptions() {
const $threadOptionsRow = $("tr#threadoptions");
if($threadOptionsRow.length < 1) return;
$threadOptionsRow.show().find("td").append(
$("<label/>").append(
$("<input/>").attr({
type: "checkbox",
name: "modstickied"
}), " Sticky thread"
),
$("<label/>").append(
$("<input/>").attr({
type: "checkbox",
name: "modlocked"
}), " Locked thread"
),
);
}
function createStaffButton() {
if($staffBtn !== null || staffInfo === null || staffInfo.rank === 0)
return;
$staffBtn = new TopBarButton("Staff", () => {
$topbar.trigger("menuButtonClick", [$staffMenu, $(document).find($staffMenu).length === 0]);
});
}
function updateReports(reports: any[]) {
// append " (#)" to the Reports link, replacing # with the number of reports
$staffMenu.find("a").each((e, elem) => {
if(elem.text.search(reportsTextRE) !== 0) return;
const $span = $("<span/>").text(` (${reports.length})`).appendTo(elem);
if(reports.length > 0) {
// make it bold and red if there are reports
$span.css({
"font-weight": "bold",
"color": "red"
});
}
});
}
function getReports() {
return $.ajax({
method: "GET",
url: `${webroot}manage/reports`,
data: {
json: "1"
},
async: true,
cache: false,
dataType: "json"
}).catch(e => {
return e;
});
}