Finished up implementing channel-based user bans.
This commit is contained in:
parent
ef79e9941c
commit
96953979a2
|
|
@ -46,6 +46,19 @@ module.exports = class{
|
||||||
//Get the active channel based on the socket
|
//Get the active channel based on the socket
|
||||||
var {activeChan, chanDB} = await this.getActiveChan(socket);
|
var {activeChan, chanDB} = await this.getActiveChan(socket);
|
||||||
|
|
||||||
|
//Check for ban
|
||||||
|
const ban = await chanDB.checkBanByUserDoc(userDB);
|
||||||
|
if(ban != null){
|
||||||
|
//Toss out banned user's
|
||||||
|
if(ban.expirationDays < 0){
|
||||||
|
socket.emit("kick", {type: "Unauthorized", reason: "You have been permanently banned from this channel!"});
|
||||||
|
}else{
|
||||||
|
socket.emit("kick", {type: "Unauthorized", reason: `You have been temporarily banned from this channel, and will be unbanned in ${ban.getDaysUntilExpiration()} day(s)!`});
|
||||||
|
}
|
||||||
|
socket.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Define listeners
|
//Define listeners
|
||||||
this.defineListeners(socket);
|
this.defineListeners(socket);
|
||||||
this.chatHandler.defineListeners(socket);
|
this.chatHandler.defineListeners(socket);
|
||||||
|
|
|
||||||
20
src/controllers/popup/channelUserBanController.js
Normal file
20
src/controllers/popup/channelUserBanController.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
//root index functions
|
||||||
|
module.exports.get = async function(req, res){
|
||||||
|
res.render('partial/popup/channelUserBan');
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,7 @@ const { Router } = require('express');
|
||||||
//local imports
|
//local imports
|
||||||
const placeholderController = require("../controllers/popup/placeholderController");
|
const placeholderController = require("../controllers/popup/placeholderController");
|
||||||
const userBanController = require("../controllers/popup/userBanController");
|
const userBanController = require("../controllers/popup/userBanController");
|
||||||
|
const channelUserBanController = require("../controllers/popup/channelUserBanController");
|
||||||
|
|
||||||
//globals
|
//globals
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
@ -28,5 +29,6 @@ const router = Router();
|
||||||
//routing functions
|
//routing functions
|
||||||
router.get('/placeholder', placeholderController.get);
|
router.get('/placeholder', placeholderController.get);
|
||||||
router.get('/userBan', userBanController.get);
|
router.get('/userBan', userBanController.get);
|
||||||
|
router.get('/channelUserBan', channelUserBanController.get);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
|
||||||
53
src/schemas/channel/channelBanSchema.js
Normal file
53
src/schemas/channel/channelBanSchema.js
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
//NPM Imports
|
||||||
|
const {mongoose} = require('mongoose');
|
||||||
|
|
||||||
|
const channelBanSchema = new mongoose.Schema({
|
||||||
|
user: {
|
||||||
|
type: mongoose.SchemaTypes.ObjectID,
|
||||||
|
required: true,
|
||||||
|
ref: "user"
|
||||||
|
},
|
||||||
|
banDate: {
|
||||||
|
type: mongoose.SchemaTypes.Date,
|
||||||
|
required: true,
|
||||||
|
default: new Date()
|
||||||
|
},
|
||||||
|
expirationDays: {
|
||||||
|
type: mongoose.SchemaTypes.Number,
|
||||||
|
required: true,
|
||||||
|
default: 14
|
||||||
|
},
|
||||||
|
banAlts: {
|
||||||
|
type: mongoose.SchemaTypes.Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//methods
|
||||||
|
channelBanSchema.methods.getDaysUntilExpiration = function(){
|
||||||
|
//Get ban date
|
||||||
|
const expirationDate = new Date(this.banDate);
|
||||||
|
//Get expiration days and calculate expiration date
|
||||||
|
expirationDate.setDate(expirationDate.getDate() + this.expirationDays);
|
||||||
|
//Calculate and return days until ban expiration
|
||||||
|
return ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = channelBanSchema;
|
||||||
|
|
@ -19,11 +19,12 @@ const {mongoose} = require('mongoose');
|
||||||
const {validationResult, matchedData} = require('express-validator');
|
const {validationResult, matchedData} = require('express-validator');
|
||||||
|
|
||||||
//Local Imports
|
//Local Imports
|
||||||
const statModel = require('../statSchema.js');
|
const statModel = require('../statSchema');
|
||||||
const {userModel} = require('../userSchema.js');
|
const {userModel} = require('../userSchema');
|
||||||
const permissionModel = require('../permissionSchema.js');
|
const permissionModel = require('../permissionSchema');
|
||||||
const channelPermissionSchema = require('./channelPermissionSchema.js');
|
const channelPermissionSchema = require('./channelPermissionSchema');
|
||||||
const { exceptionHandler } = require('../../utils/loggerUtils.js');
|
const channelBanSchema = require('./channelBanSchema');
|
||||||
|
const { exceptionHandler } = require('../../utils/loggerUtils');
|
||||||
|
|
||||||
const channelSchema = new mongoose.Schema({
|
const channelSchema = new mongoose.Schema({
|
||||||
id: {
|
id: {
|
||||||
|
|
@ -69,28 +70,7 @@ const channelSchema = new mongoose.Schema({
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
//Thankfully we don't have to keep track of alts, ips, or deleted users so this should be a little easier :P
|
//Thankfully we don't have to keep track of alts, ips, or deleted users so this should be a little easier :P
|
||||||
banList: [{
|
banList: [channelBanSchema]
|
||||||
user: {
|
|
||||||
type: mongoose.SchemaTypes.ObjectID,
|
|
||||||
required: true,
|
|
||||||
ref: "user"
|
|
||||||
},
|
|
||||||
banDate: {
|
|
||||||
type: mongoose.SchemaTypes.Date,
|
|
||||||
required: true,
|
|
||||||
default: new Date()
|
|
||||||
},
|
|
||||||
expirationDays: {
|
|
||||||
type: mongoose.SchemaTypes.Number,
|
|
||||||
required: true,
|
|
||||||
default: 14
|
|
||||||
},
|
|
||||||
banAlts: {
|
|
||||||
type: mongoose.SchemaTypes.Boolean,
|
|
||||||
required: true,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -194,6 +174,21 @@ channelSchema.statics.reqPermCheck = function(perm, chanField = "chanName"){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channelSchema.statics.processExpiredBans = async function(){
|
||||||
|
const chanDB = await this.find({});
|
||||||
|
|
||||||
|
chanDB.forEach((channel) => {
|
||||||
|
channel.banList.forEach(async (ban, i) => {
|
||||||
|
//ignore permanent and non-expired bans
|
||||||
|
if(ban.expirationDays >= 0 && ban.getDaysUntilExpiration() <= 0){
|
||||||
|
//Get the index of the ban
|
||||||
|
channel.banList.splice(i,1);
|
||||||
|
return await channel.save();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//methods
|
//methods
|
||||||
channelSchema.methods.updateSettings = async function(settingsMap){
|
channelSchema.methods.updateSettings = async function(settingsMap){
|
||||||
settingsMap.forEach((value, key) => {
|
settingsMap.forEach((value, key) => {
|
||||||
|
|
@ -372,6 +367,7 @@ channelSchema.methods.getChanBans = async function(){
|
||||||
var banObj = {
|
var banObj = {
|
||||||
banDate: ban.banDate,
|
banDate: ban.banDate,
|
||||||
expirationDays: ban.expirationDays,
|
expirationDays: ban.expirationDays,
|
||||||
|
banAlts: ban.banAlts,
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if the ban was permanent (expiration set before ban date)
|
//Check if the ban was permanent (expiration set before ban date)
|
||||||
|
|
@ -382,6 +378,7 @@ channelSchema.methods.getChanBans = async function(){
|
||||||
|
|
||||||
//Set calculated expiration date
|
//Set calculated expiration date
|
||||||
banObj.expirationDate = expirationDate;
|
banObj.expirationDate = expirationDate;
|
||||||
|
banObj.daysUntilExpiration = ban.getDaysUntilExpiration();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setup user object (Do this last to keep it at bottom for human-readibility of json :P)
|
//Setup user object (Do this last to keep it at bottom for human-readibility of json :P)
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,7 @@ userBanSchema.statics.getBans = async function(){
|
||||||
banDate: ban.banDate,
|
banDate: ban.banDate,
|
||||||
expirationDays: ban.expirationDays,
|
expirationDays: ban.expirationDays,
|
||||||
expirationDate: expirationDate,
|
expirationDate: expirationDate,
|
||||||
|
daysUntilExpiration: ban.getDaysUntilExpiration(),
|
||||||
user: userObj,
|
user: userObj,
|
||||||
ips: ban.ips,
|
ips: ban.ips,
|
||||||
alts: ban.alts,
|
alts: ban.alts,
|
||||||
|
|
@ -247,7 +248,7 @@ userBanSchema.methods.getDaysUntilExpiration = function(){
|
||||||
//Get expiration days and calculate expiration date
|
//Get expiration days and calculate expiration date
|
||||||
expirationDate.setDate(expirationDate.getDate() + this.expirationDays);
|
expirationDate.setDate(expirationDate.getDate() + this.expirationDays);
|
||||||
//Calculate and return days until ban expiration
|
//Calculate and return days until ban expiration
|
||||||
return daysUntilExpiraiton = ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
return ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = mongoose.model("userBan", userBanSchema);
|
module.exports = mongoose.model("userBan", userBanSchema);
|
||||||
|
|
@ -18,9 +18,20 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
const cron = require('node-cron');
|
const cron = require('node-cron');
|
||||||
|
|
||||||
//Local Imports
|
//Local Imports
|
||||||
const userBanSchema = require('../schemas/userBanSchema');
|
const userBanModel = require('../schemas/userBanSchema');
|
||||||
|
const channelModel = require('../schemas/channel/channelSchema');
|
||||||
|
|
||||||
|
module.exports.schedule = function(){
|
||||||
|
//Process expired global bans every night at midnight
|
||||||
|
cron.schedule('0 0 * * *', ()=>{userBanModel.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.kickoff = function(){
|
module.exports.kickoff = function(){
|
||||||
//Process expired bans every night at midnight
|
//Process expired global bans that may have expired since last restart
|
||||||
cron.schedule('0 0 * * *', ()=>{userBanSchema.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
userBanModel.processExpiredBans()
|
||||||
|
//Process expired channel bans that may haven ot expired since last restart
|
||||||
|
channelModel.processExpiredBans();
|
||||||
|
|
||||||
|
//Schedule jobs
|
||||||
|
module.exports.schedule();
|
||||||
}
|
}
|
||||||
|
|
@ -23,10 +23,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
<body>
|
<body>
|
||||||
<%- include('partial/navbar', {user}); %>
|
<%- include('partial/navbar', {user}); %>
|
||||||
<h1 class="panel-title"><%= instance %> Admin Panel</h1>
|
<h1 class="panel-title"><%= instance %> Admin Panel</h1>
|
||||||
<%- include('partial/adminPanel/channelList', {chanGuide}) %>
|
<div class="admin-panel-container-div">
|
||||||
<%- include('partial/adminPanel/userList', {user, userList, rankEnum}) %>
|
<%- include('partial/adminPanel/channelList', {chanGuide}) %>
|
||||||
<%- include('partial/adminPanel/permList', {permList, rankEnum}) %>
|
<%- include('partial/adminPanel/userList', {user, userList, rankEnum}) %>
|
||||||
<%- include('partial/adminPanel/userBanList') %>
|
<%- include('partial/adminPanel/permList', {permList, rankEnum}) %>
|
||||||
|
<%- include('partial/adminPanel/userBanList') %>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<footer>
|
<footer>
|
||||||
<%- include('partial/scripts', {user}); %>
|
<%- include('partial/scripts', {user}); %>
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
<body>
|
<body>
|
||||||
<%- include('partial/navbar', {user}); %>
|
<%- include('partial/navbar', {user}); %>
|
||||||
<h1 class="panel-title"><%- channel.name %> - Channel Settings</h1>
|
<h1 class="panel-title"><%- channel.name %> - Channel Settings</h1>
|
||||||
<%- include('partial/channelSettings/userList.ejs'); %>
|
<div class="admin-panel-container-div">
|
||||||
<%- include('partial/channelSettings/settings.ejs'); %>
|
<%- include('partial/channelSettings/userList.ejs'); %>
|
||||||
<%- include('partial/channelSettings/permList.ejs'); %>
|
<%- include('partial/channelSettings/banList.ejs'); %>
|
||||||
<a href="javascript:" id="channel-delete">Delete Channel</a>
|
<%- include('partial/channelSettings/settings.ejs'); %>
|
||||||
|
<%- include('partial/channelSettings/permList.ejs'); %>
|
||||||
|
</div>
|
||||||
|
<button href="javascript:" id="channel-delete">Delete Channel</button>
|
||||||
<footer>
|
<footer>
|
||||||
<%- include('partial/scripts', {user}); %>
|
<%- include('partial/scripts', {user}); %>
|
||||||
<script src="/js/channelSettings.js"></script>
|
<script src="/js/channelSettings.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -27,19 +27,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
<h3>Name</h3>
|
<h3>Name</h3>
|
||||||
</td>
|
</td>
|
||||||
<td id="admin-ban-list-entry-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
<td id="admin-ban-list-entry-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
||||||
<h3>Sign-Up Date</h3>
|
<h3>Sign-Up<br>Date</h3>
|
||||||
</td>
|
</td>
|
||||||
<td id="admin-ban-list-entry-ban-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
<td id="admin-ban-list-entry-ban-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
||||||
<h3>Ban Date</h3>
|
<h3>Ban<br>Date</h3>
|
||||||
</td>
|
</td>
|
||||||
<td id="admin-ban-list-entry-expiration-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
<td id="admin-ban-list-entry-expiration-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
||||||
<h3>Expiration Date</h3>
|
<h3>Expires</h3>
|
||||||
</td>
|
</td>
|
||||||
<td id="admin-ban-list-entry-expiration-type-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
<td id="admin-ban-list-entry-expiration-type-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
||||||
<h3>Expiration Action</h3>
|
<h3>Expiration<br>Action</h3>
|
||||||
</td>
|
</td>
|
||||||
<td id="admin-ban-list-entry-actions-type-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
<td id="admin-ban-list-entry-actions-type-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
||||||
<h3>Actions</h3>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
<h3>E-Mail</h3>
|
<h3>E-Mail</h3>
|
||||||
</td>
|
</td>
|
||||||
<td id="admin-user-list-entry-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
<td id="admin-user-list-entry-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
||||||
<h3>Sign-Up Date</h3>
|
<h3>Sign-Up<br>Date</h3>
|
||||||
</td>
|
</td>
|
||||||
<td id="admin-user-list-entry-control-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
<td id="admin-user-list-entry-control-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
|
||||||
<h3>Actions</h3>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% userList.forEach((curUser) => { %>
|
<% userList.forEach((curUser) => { %>
|
||||||
|
|
|
||||||
34
src/views/partial/channelSettings/banList.ejs
Normal file
34
src/views/partial/channelSettings/banList.ejs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!--Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
|
<div class="admin-list-div">
|
||||||
|
<h3 id="channel-ban-title">Channel Bans:</h3>
|
||||||
|
<span id="new-ban-span" class="admin-list-new-span">
|
||||||
|
<input placeholder="Add Username..." id="new-ban-input" class="admin-list-new-input">
|
||||||
|
<button id="new-ban-button">Ban</button>
|
||||||
|
</span>
|
||||||
|
<table id="admin-ban-list-table" class="admin-list-table">
|
||||||
|
<tr class="admin-list-entry-title-row">
|
||||||
|
<td class="admin-list-entry-item admin-list-entry admin-list-entry-img-title"><h3>Img</h3></td>
|
||||||
|
<td id="admin-user-list-entry-id-title" class="admin-list-entry-item admin-list-entry admin-list-entry-not-first-col"><h3>ID</h3></td>
|
||||||
|
<td class="admin-list-entry-item admin-list-entry admin-list-entry-not-first-col"><h3>Name</h3></td>
|
||||||
|
<td class="admin-list-entry-item admin-list-entry admin-list-entry-not-first-col"><h3>Ban<br>Alts</h3></td>
|
||||||
|
<td id="channel-user-list-entry-ban-date-title" class="admin-list-entry-item admin-list-entry admin-list-entry-not-first-col"><h3>Ban Date</h3></td>
|
||||||
|
<td id="channel-user-list-entry-expiration-date-title" class="admin-list-entry-item admin-list-entry admin-list-entry-not-first-col"><h3>Expires</h3></td>
|
||||||
|
<td id="channel-user-list-entry-actions-title" class="admin-list-entry-item admin-list-entry admin-list-entry-not-first-col"> </td>
|
||||||
|
</tr>
|
||||||
|
<!-- This is getting filled via AJAX since we need to refresh it when new users are added anywho. -->
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
@ -16,8 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
<div class="admin-list-div">
|
<div class="admin-list-div">
|
||||||
<h3 id="channel-rank-title">Channel Ranks:</h3>
|
<h3 id="channel-rank-title">Channel Ranks:</h3>
|
||||||
<sup id="channel-rank-sup">Users set to default channel rank 'user' will NOT be listed below.</sup>
|
<sup id="channel-rank-sup">Users set to default channel rank 'user' will NOT be listed below.</sup>
|
||||||
<span id="new-rank-span">
|
<span id="new-rank-span" class="admin-list-new-span">
|
||||||
<input placeholder="Add Username..." id="new-rank-input">
|
<input placeholder="Add Username..." id="new-rank-input" class="admin-list-new-input">
|
||||||
<select id="new-rank-select">
|
<select id="new-rank-select">
|
||||||
<%rankEnum.slice().reverse().forEach((rank)=>{ %>
|
<%rankEnum.slice().reverse().forEach((rank)=>{ %>
|
||||||
<option value="<%= rank %>"><%= rank %></option>
|
<option value="<%= rank %>"><%= rank %></option>
|
||||||
|
|
|
||||||
39
src/views/partial/popup/channelUserBan.ejs
Normal file
39
src/views/partial/popup/channelUserBan.ejs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!--Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
|
<!-- I could turn this into partials which get called by both this and userBan but that seems like a lot of work to avoid a quick copy/pase
|
||||||
|
even if I'm usually against that, sometimes you gotta break da rulez :P -->
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/popup/userBan.css">
|
||||||
|
<h3 class="popup-title">Ban NULL</h3>
|
||||||
|
<form class="ban-popup-content" action="javascript:">
|
||||||
|
<span>
|
||||||
|
<label for="ban-popup-perm">Perma-Ban:</label>
|
||||||
|
<input type="checkbox" name="ban-popup-perm" id="ban-popup-perm">
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<label id="ban-popup-expiration-prefix" for="ban-popup-expiration">Ban Expires In: </label>
|
||||||
|
<input type="number" name="ban-popup-expiration" id="ban-popup-expiration" value="14">
|
||||||
|
<label fid="ban-popup-expiration-postfix" or="ban-popup-expiration"> Days</label>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<label id="ban-popup-alts-prefix" for="ban-popup-alts">Ban Alts: </label>
|
||||||
|
<input type="checkbox" name="ban-popup-alts" id="ban-popup-alts" checked>
|
||||||
|
</span>
|
||||||
|
<span id="ban-popup-button-span">
|
||||||
|
<button id="ban-popup-cancel-button">Cancel</button>
|
||||||
|
<span id="ban-popup-button-span-spacer"></span>
|
||||||
|
<button id="ban-popup-ban-button">Ban</button>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
|
@ -19,8 +19,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-list-div{
|
.admin-panel-container-div{
|
||||||
margin: 0 30%;
|
margin: 0 auto;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-list-field{
|
.admin-list-field{
|
||||||
|
|
@ -94,14 +95,14 @@ img.admin-list-entry-item{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#new-rank-span{
|
.admin-list-new-span{
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#new-rank-input{
|
.admin-list-new-input{
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,3 +115,20 @@ img.admin-list-entry-item{
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 0 0.2em;
|
margin: 0 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#new-ban-button{
|
||||||
|
margin: auto;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#channel-delete{
|
||||||
|
margin: auto auto 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#admin-ban-list-entry-expiration-type-title{
|
||||||
|
width: 8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#admin-ban-list-entry-expiration-date-title{
|
||||||
|
width: 11em;
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
--accent0: rgb(48, 47, 47);
|
--accent0: rgb(48, 47, 47);
|
||||||
--accent0-alt0: rgb(34, 34, 34);
|
--accent0-alt0: rgb(34, 34, 34);
|
||||||
--accent0-warning: firebrick;
|
--accent0-warning: firebrick;
|
||||||
|
--accent0-warning-alt0: rgb(121, 11, 11);
|
||||||
--accent1: rgb(245, 245, 245);
|
--accent1: rgb(245, 245, 245);
|
||||||
--accent1-alt0: rgb(185, 185, 185);
|
--accent1-alt0: rgb(185, 185, 185);
|
||||||
--accent2: var(--accent0-alt0);
|
--accent2: var(--accent0-alt0);
|
||||||
|
|
@ -130,8 +131,18 @@ a#account-settings-delete-link{
|
||||||
color: var(--accent0-warning);
|
color: var(--accent0-warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
#channel-delete{
|
#channel-delete, #ban-popup-ban-button, #new-ban-button{
|
||||||
color: var(--accent0-warning);
|
background-color: var(--accent0-warning);
|
||||||
|
color: var(--accent1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#channel-delete:hover, #ban-popup-ban-button:hover, #new-ban-button:hover{
|
||||||
|
background-color: var(--accent0-warning-alt0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#channel-delete:active, #ban-popup-ban-button:active, #new-ban-button:active{
|
||||||
|
background-color: var(--accent0-warning-alt0);
|
||||||
|
color: var(--accent1-alt0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-list-field,.admin-list-table{
|
.admin-list-field,.admin-list-table{
|
||||||
|
|
@ -265,20 +276,20 @@ select.panel-head-element{
|
||||||
border-bottom: solid 1px var(--accent0);
|
border-bottom: solid 1px var(--accent0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#new-rank-span{
|
.admin-list-new-span{
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#new-rank-span:focus-within{
|
.admin-list-new-span:focus-within{
|
||||||
box-shadow: 2px 2px 3px var(--focus0), -2px 2px 3px var(--focus0), 2px -2px 3px var(--focus0), -2px -2px 3px var(--focus0);
|
box-shadow: 2px 2px 3px var(--focus0), -2px 2px 3px var(--focus0), 2px -2px 3px var(--focus0), -2px -2px 3px var(--focus0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#new-rank-input{
|
.admin-list-new-input{
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#new-rank-input:focus{
|
.admin-list-new-input:focus{
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
@ -295,8 +306,3 @@ select.panel-head-element{
|
||||||
box-shadow: 3px 3px 1px var(--bg1-alt0) inset;
|
box-shadow: 3px 3px 1px var(--bg1-alt0) inset;
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ban-popup-ban-button{
|
|
||||||
background-color: var(--accent0-warning);
|
|
||||||
color: var(--accent1);
|
|
||||||
}
|
|
||||||
|
|
@ -21,8 +21,9 @@ class canopyAdminUtils{
|
||||||
|
|
||||||
//Statics
|
//Statics
|
||||||
static banUserPopup = class{
|
static banUserPopup = class{
|
||||||
constructor(target){
|
constructor(target, cb){
|
||||||
this.target = target;
|
this.target = target;
|
||||||
|
this.cb = cb;
|
||||||
this.popup = new canopyUXUtils.popup("userBan", true, this.asyncConstruction.bind(this));
|
this.popup = new canopyUXUtils.popup("userBan", true, this.asyncConstruction.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,12 +62,12 @@ class canopyAdminUtils{
|
||||||
this.popup.closePopup();
|
this.popup.closePopup();
|
||||||
|
|
||||||
//Submit the user ban based on input
|
//Submit the user ban based on input
|
||||||
const bans = await adminUtil.banUser(this.target, this.permBan.checked, this.expiration.value);
|
const data = await adminUtil.banUser(this.target, this.permBan.checked, this.expiration.value);
|
||||||
|
|
||||||
//For some reason comparing this against undefined or null wasnt working in and of itself...
|
//For some reason comparing this against undefined or null wasnt working in and of itself...
|
||||||
if(typeof userBanList != "undefined" && bans != null){
|
if(data != null){
|
||||||
//Why add an extra get request when we already have the data? :P
|
//Why add an extra get request when we already have the data? :P
|
||||||
await userBanList.renderBanList(bans);
|
await this.cb(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,8 +198,7 @@ class adminUserList{
|
||||||
|
|
||||||
banPopup(event){
|
banPopup(event){
|
||||||
const user = event.target.id.replace("admin-user-list-ban-icon-","");
|
const user = event.target.id.replace("admin-user-list-ban-icon-","");
|
||||||
|
new canopyAdminUtils.banUserPopup(user, userBanList.renderBanList.bind(userBanList));
|
||||||
new canopyAdminUtils.banUserPopup(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelect(update, select){
|
updateSelect(update, select){
|
||||||
|
|
@ -283,14 +283,10 @@ class adminUserBanList{
|
||||||
|
|
||||||
renderBanList(banList){
|
renderBanList(banList){
|
||||||
this.clearBanList();
|
this.clearBanList();
|
||||||
console.log(banList);
|
|
||||||
banList.forEach((ban) => {
|
banList.forEach((ban) => {
|
||||||
//Calculate expiration date and expiration days
|
//Calculate expiration date and expiration days
|
||||||
const expirationDate = new Date(ban.expirationDate);
|
var expirationDateString = `${new Date(ban.expirationDate).toDateString()}<br>(${ban.daysUntilExpiration} day(s) left)`;
|
||||||
const expirationDays = ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
var banActionString = ban.permanent ? "Nuke<br>Accounts" : "Un-Ban";
|
||||||
var expirationDateString = `${expirationDate.toDateString()} (${expirationDays} day(s) left)`;
|
|
||||||
var banActionString = ban.permanent ? "Account Deletion" : "Un-Ban";
|
|
||||||
console.log(ban);
|
|
||||||
if(ban.user == null){
|
if(ban.user == null){
|
||||||
//Fudge the user object if it's already been deleted
|
//Fudge the user object if it's already been deleted
|
||||||
ban.user = {
|
ban.user = {
|
||||||
|
|
@ -302,8 +298,8 @@ class adminUserBanList{
|
||||||
|
|
||||||
//Fake the display string
|
//Fake the display string
|
||||||
var signUpDateString = "-"
|
var signUpDateString = "-"
|
||||||
expirationDateString = "Accounts Nuked"
|
expirationDateString = "Accounts<br>Nuked"
|
||||||
banActionString = "Accounts Nuked"
|
banActionString = "Accounts<br>Nuked"
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
var signUpDateString = new Date(ban.user.date).toDateString()
|
var signUpDateString = new Date(ban.user.date).toDateString()
|
||||||
|
|
@ -331,59 +327,28 @@ class adminUserBanList{
|
||||||
nukeAccount.title = `Nuke accounts`;
|
nukeAccount.title = `Nuke accounts`;
|
||||||
nukeAccount.addEventListener("click",console.log);
|
nukeAccount.addEventListener("click",console.log);
|
||||||
|
|
||||||
//Append cells to row
|
//append img cell to row
|
||||||
entryRow.appendChild(newCell(imgNode, true));
|
entryRow.appendChild(utils.ux.newTableCell(imgNode, true));
|
||||||
entryRow.appendChild(newCell(ban.user.id));
|
|
||||||
entryRow.appendChild(newCell(ban.user.user));
|
//Append standard cells to row
|
||||||
entryRow.appendChild(newCell(signUpDateString));
|
[
|
||||||
entryRow.appendChild(newCell(new Date(ban.banDate).toDateString()));
|
ban.user.id,
|
||||||
entryRow.appendChild(newCell(expirationDateString));
|
ban.user.user,
|
||||||
entryRow.appendChild(newCell(banActionString));
|
signUpDateString,
|
||||||
entryRow.appendChild(newCell(ban.user.deleted ? unbanIcon : [unbanIcon, nukeAccount]));
|
new Date(ban.banDate).toDateString(),
|
||||||
|
expirationDateString,
|
||||||
|
banActionString,
|
||||||
|
(ban.user.deleted ? unbanIcon : [unbanIcon, nukeAccount])
|
||||||
|
].forEach((content)=>{
|
||||||
|
//I don't like repeating myself, and this didn't really need it's own function
|
||||||
|
//though we could make one where each is an object that contains ever property needed and pass it to a mktable function
|
||||||
|
//I just don't see us using it enough to justify it :P
|
||||||
|
entryRow.appendChild(utils.ux.newTableCell(content));
|
||||||
|
});
|
||||||
|
|
||||||
//Append row to table
|
//Append row to table
|
||||||
this.table.appendChild(entryRow);
|
this.table.appendChild(entryRow);
|
||||||
});
|
});
|
||||||
|
|
||||||
//We should really move this over to uxutils along with newrow & newtable functions
|
|
||||||
function newCell(content, firstCol = false){
|
|
||||||
//Create a new 'td' element
|
|
||||||
const cell = document.createElement('td');
|
|
||||||
cell.classList.add("admin-list-entry","admin-list-entry-item");
|
|
||||||
|
|
||||||
//If it's not the first column, mention it!
|
|
||||||
if(!firstCol){
|
|
||||||
cell.classList.add("admin-list-entry-not-first-col");
|
|
||||||
}
|
|
||||||
|
|
||||||
//check for arrays
|
|
||||||
if(content.forEach == null){
|
|
||||||
//add single items
|
|
||||||
addContent(content);
|
|
||||||
}else{
|
|
||||||
//Crawl through content array
|
|
||||||
content.forEach((item)=>{
|
|
||||||
//add each item
|
|
||||||
addContent(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//return the resulting cell
|
|
||||||
return cell;
|
|
||||||
|
|
||||||
|
|
||||||
function addContent(ct){
|
|
||||||
//If we're adding as node
|
|
||||||
if(ct.cloneNode != null){
|
|
||||||
//append it like it's a node
|
|
||||||
cell.appendChild(ct);
|
|
||||||
}else{
|
|
||||||
//otherwise use it as innerHTML
|
|
||||||
cell.innerHTML = ct;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,11 @@ class channelSettingsPage{
|
||||||
//Get channel name off of the URL
|
//Get channel name off of the URL
|
||||||
this.channel = window.location.pathname.slice(3).replace('/settings','');
|
this.channel = window.location.pathname.slice(3).replace('/settings','');
|
||||||
//Instantiate UX handling objects, making sure to pass the channel name.
|
//Instantiate UX handling objects, making sure to pass the channel name.
|
||||||
this.deleteBtn = new deleteBtn(this.channel);
|
|
||||||
this.rankList = new rankList(this.channel);
|
this.rankList = new rankList(this.channel);
|
||||||
this.prefrenceList = new prefrenceList(this.channel);
|
this.banList = new banList(this.channel);
|
||||||
this.permList = new permList(this.channel);
|
this.permList = new permList(this.channel);
|
||||||
|
this.prefrenceList = new prefrenceList(this.channel);
|
||||||
|
this.deleteBtn = new deleteBtn(this.channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,15 +43,6 @@ class rankList{
|
||||||
this.userPrompt.addEventListener("keydown", this.submitNewRank.bind(this));
|
this.userPrompt.addEventListener("keydown", this.submitNewRank.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
resetInput(){
|
|
||||||
//These change every time the table is refreshed, so we should reset the property as well.
|
|
||||||
this.inputs = document.querySelectorAll(".channel-rank-select");
|
|
||||||
|
|
||||||
this.inputs.forEach((input) => {
|
|
||||||
input.addEventListener("change", this.submitUpdate.bind(this));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadList(){
|
async loadList(){
|
||||||
const list = await utils.ajax.getChannelRank(this.channel);
|
const list = await utils.ajax.getChannelRank(this.channel);
|
||||||
|
|
||||||
|
|
@ -87,7 +79,7 @@ class rankList{
|
||||||
//Do not pass go, do not collect $200
|
//Do not pass go, do not collect $200
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//Get rank of logged in user
|
//Get name/rank of logged in user
|
||||||
const curName = document.querySelector("#username").textContent
|
const curName = document.querySelector("#username").textContent
|
||||||
const curUser = data[curName];
|
const curUser = data[curName];
|
||||||
const rankEnum = await utils.ajax.getRankEnum();
|
const rankEnum = await utils.ajax.getRankEnum();
|
||||||
|
|
@ -95,47 +87,25 @@ class rankList{
|
||||||
//clear the table
|
//clear the table
|
||||||
this.clearTable();
|
this.clearTable();
|
||||||
|
|
||||||
|
//For each user in the list
|
||||||
Object.entries(data).forEach((userAr) => {
|
Object.entries(data).forEach((userAr) => {
|
||||||
//pull user object from entry array
|
//pull user object from entry array
|
||||||
const user = userAr[1];
|
const user = userAr[1];
|
||||||
|
|
||||||
//Create entry row
|
|
||||||
const entryRow = document.createElement('tr');
|
|
||||||
entryRow.classList.add("admin-list-entry");
|
|
||||||
|
|
||||||
//Create IMG cell
|
|
||||||
const imgCell = document.createElement('td')
|
|
||||||
imgCell.classList.add("admin-list-entry","admin-list-entry-item");
|
|
||||||
|
|
||||||
//Create IMG node inside of IMG cell
|
//Create IMG node inside of IMG cell
|
||||||
const imgNode = document.createElement('img');
|
const imgNode = document.createElement('img');
|
||||||
imgNode.classList.add("admin-list-entry","admin-list-entry-item");
|
imgNode.classList.add("admin-list-entry","admin-list-entry-item");
|
||||||
imgNode.src = user.img;
|
imgNode.src = user.img;
|
||||||
|
|
||||||
//append Img Node to Img Cell
|
|
||||||
imgCell.appendChild(imgNode);
|
|
||||||
|
|
||||||
//Create ID cell
|
|
||||||
const idCell = document.createElement('td');
|
|
||||||
idCell.classList.add("admin-list-entry","admin-list-entry-item","admin-list-entry-not-first-col");
|
|
||||||
idCell.innerHTML = user.id;
|
|
||||||
|
|
||||||
//Create Name cell
|
|
||||||
const nameCell = document.createElement('td');
|
|
||||||
nameCell.classList.add("admin-list-entry","admin-list-entry-item","admin-list-entry-not-first-col");
|
|
||||||
nameCell.innerHTML = user.user;
|
|
||||||
|
|
||||||
//Create Rank cell
|
|
||||||
const rankCell = document.createElement('td');
|
|
||||||
rankCell.classList.add("admin-list-entry","admin-list-entry-item","admin-list-entry-not-first-col");
|
|
||||||
//If the listed user rank is equal or higher than the signed-in user
|
//If the listed user rank is equal or higher than the signed-in user
|
||||||
if(rankEnum.indexOf(user.rank) >= rankEnum.indexOf(curUser.rank)){
|
if(rankEnum.indexOf(user.rank) >= rankEnum.indexOf(curUser.rank)){
|
||||||
rankCell.innerHTML = user.rank;
|
var rankContent = user.rank;
|
||||||
}else{
|
}else{
|
||||||
//Create rank select
|
//Create rank select
|
||||||
const rankSelect = document.createElement('select');
|
var rankContent = document.createElement('select');
|
||||||
rankSelect.id = `channel-rank-select-${user.user}`
|
rankContent.id = `channel-rank-select-${user.user}`
|
||||||
rankSelect.classList.add("channel-rank-select")
|
rankContent.classList.add("channel-rank-select")
|
||||||
|
rankContent.addEventListener("change", this.submitUpdate.bind(this));
|
||||||
|
|
||||||
//for each rank in the enum
|
//for each rank in the enum
|
||||||
rankEnum.slice().reverse().forEach((rank) => {
|
rankEnum.slice().reverse().forEach((rank) => {
|
||||||
|
|
@ -144,24 +114,17 @@ class rankList{
|
||||||
rankOption.value = rank;
|
rankOption.value = rank;
|
||||||
rankOption.innerHTML = rank;
|
rankOption.innerHTML = rank;
|
||||||
rankOption.selected = user.rank == rank;
|
rankOption.selected = user.rank == rank;
|
||||||
rankSelect.appendChild(rankOption);
|
rankContent.appendChild(rankOption);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Set value to current user rank and append it to Rank Cell
|
|
||||||
rankCell.appendChild(rankSelect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Append cells to row
|
//Generate row and append to table
|
||||||
entryRow.appendChild(imgCell);
|
this.table.appendChild(utils.ux.newTableRow([
|
||||||
entryRow.appendChild(idCell);
|
imgNode,
|
||||||
entryRow.appendChild(nameCell);
|
user.id,
|
||||||
entryRow.appendChild(rankCell);
|
user.user,
|
||||||
|
rankContent
|
||||||
//Append row to table
|
]));
|
||||||
this.table.appendChild(entryRow);
|
|
||||||
|
|
||||||
//reset input events
|
|
||||||
this.resetInput();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,6 +140,137 @@ class rankList{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class banList{
|
||||||
|
constructor(channel){
|
||||||
|
this.channel = channel;
|
||||||
|
this.table = document.querySelector("#admin-ban-list-table");
|
||||||
|
this.banPrompt = document.querySelector("#new-ban-input");
|
||||||
|
this.banButton = document.querySelector("#new-ban-button");
|
||||||
|
|
||||||
|
this.loadList();
|
||||||
|
this.setupInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupInput(){
|
||||||
|
this.banButton.addEventListener('click', this.ban.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadList(){
|
||||||
|
const data = await utils.ajax.getChanBans(this.channel);
|
||||||
|
this.updateList(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearList(){
|
||||||
|
const oldRows = this.table.querySelectorAll('tr.admin-list-entry');
|
||||||
|
oldRows.forEach((row) => {
|
||||||
|
row.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async ban(event){
|
||||||
|
new banUserPopup(this.channel, this.banPrompt.value, this.updateList.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async unban(event){
|
||||||
|
//Rip user outta the target id
|
||||||
|
const user = event.target.id.replace("admin-user-list-unban-icon-","");
|
||||||
|
//Tell the server to unban them and get the list returned
|
||||||
|
const list = await utils.ajax.chanUnban(this.channel, user);
|
||||||
|
|
||||||
|
//Use the list to update the UI
|
||||||
|
this.updateList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateList(bans){
|
||||||
|
//for each ban listed
|
||||||
|
this.clearList();
|
||||||
|
bans.forEach((ban)=>{
|
||||||
|
//Create IMG node
|
||||||
|
const imgNode = document.createElement('img');
|
||||||
|
imgNode.classList.add("admin-list-entry","admin-list-entry-item");
|
||||||
|
imgNode.src = ban.user.img;
|
||||||
|
|
||||||
|
//Create banAlts check
|
||||||
|
const banAlts = document.createElement('i');
|
||||||
|
banAlts.classList.add("admin-user-list-ban-alts",ban.banAlts ? "bi-check" : "bi-x");
|
||||||
|
|
||||||
|
//Create unban icon node
|
||||||
|
const unbanIcon = document.createElement('i');
|
||||||
|
unbanIcon.classList.add("bi-emoji-smile-fill","admin-user-list-icon","admin-user-list-unban-icon");
|
||||||
|
unbanIcon.id = `admin-user-list-unban-icon-${ban.user.user}`;
|
||||||
|
unbanIcon.title = `Unban ${ban.user.user}`;
|
||||||
|
unbanIcon.addEventListener("click", this.unban.bind(this));
|
||||||
|
|
||||||
|
//THIS IS NOT YET PROPERLY IMPLEMENTED AS getDaysUntilExpiration has not been made for chan bans
|
||||||
|
const expirationString = ban.expirationDays < 0 ? "Never" : `${new Date(ban.expirationDate).toDateString()}<br>(${ban.daysUntilExpiration} day(s) left)`;
|
||||||
|
|
||||||
|
//Generate row and append to table
|
||||||
|
this.table.appendChild(utils.ux.newTableRow([
|
||||||
|
imgNode,
|
||||||
|
ban.user.id,
|
||||||
|
ban.user.user,
|
||||||
|
banAlts,
|
||||||
|
new Date(ban.banDate).toDateString(),
|
||||||
|
expirationString,
|
||||||
|
unbanIcon
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class banUserPopup{
|
||||||
|
constructor(channel, target, cb){
|
||||||
|
this.channel = channel;
|
||||||
|
this.target = target;
|
||||||
|
this.cb = cb;
|
||||||
|
this.popup = new canopyUXUtils.popup("channelUserBan", true, this.asyncConstruction.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncConstruction(){
|
||||||
|
this.title = document.querySelector(".popup-title");
|
||||||
|
//Setup title text real quick-like :P
|
||||||
|
this.title.innerHTML = `Ban ${this.target}`;
|
||||||
|
|
||||||
|
this.permBan = document.querySelector("#ban-popup-perm");
|
||||||
|
this.expiration = document.querySelector("#ban-popup-expiration");
|
||||||
|
this.expirationPrefix = document.querySelector("#ban-popup-expiration-prefix");
|
||||||
|
this.banAlts = document.querySelector("#ban-popup-alts");
|
||||||
|
this.banButton = document.querySelector("#ban-popup-ban-button");
|
||||||
|
this.cancelButton = document.querySelector("#ban-popup-cancel-button");
|
||||||
|
|
||||||
|
this.setupInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupInput(){
|
||||||
|
this.permBan.addEventListener("change", this.permaBanLabel.bind(this));
|
||||||
|
this.cancelButton.addEventListener("click", this.popup.closePopup.bind(this.popup));
|
||||||
|
this.banButton.addEventListener("click",this.ban.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
permaBanLabel(event){
|
||||||
|
if(event.target.checked){
|
||||||
|
this.expiration.disabled = true;
|
||||||
|
}else{
|
||||||
|
this.expiration.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async ban(event){
|
||||||
|
//Get expiration days
|
||||||
|
const expirationDays = this.permBan.checked ? -1 : this.expiration.value;
|
||||||
|
//Send ban request off to server and retrieve new ban list
|
||||||
|
const data = await utils.ajax.chanBan(this.channel, this.target, expirationDays, this.banAlts.checked);
|
||||||
|
|
||||||
|
//Close the popup
|
||||||
|
this.popup.closePopup();
|
||||||
|
|
||||||
|
//If we have data and a callback, run the callback with our data
|
||||||
|
if(data != null && this.cb != null){
|
||||||
|
await this.cb(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class prefrenceList{
|
class prefrenceList{
|
||||||
constructor(channel){
|
constructor(channel){
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
|
|
@ -206,26 +300,6 @@ class prefrenceList{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class deleteBtn{
|
|
||||||
constructor(channel){
|
|
||||||
this.channel = channel;
|
|
||||||
this.delete = document.querySelector("#channel-delete");
|
|
||||||
|
|
||||||
this.delete.addEventListener('click', this.promptDelete.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
promptDelete(){
|
|
||||||
var confirm = window.prompt(`Warning: You are about to nuke ${this.channel} off of the face of the fucking planet, no taksie-backsies. \n \n Type in ${this.channel} to confirm.`);
|
|
||||||
this.deleteChannel(confirm);
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteChannel(confirm){
|
|
||||||
if(this.channel === confirm){
|
|
||||||
utils.ajax.deleteChannel(this.channel, confirm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class permList{
|
class permList{
|
||||||
constructor(channel){
|
constructor(channel){
|
||||||
this.channel = channel
|
this.channel = channel
|
||||||
|
|
@ -254,4 +328,24 @@ class permList{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new channelSettingsPage();
|
class deleteBtn{
|
||||||
|
constructor(channel){
|
||||||
|
this.channel = channel;
|
||||||
|
this.delete = document.querySelector("#channel-delete");
|
||||||
|
|
||||||
|
this.delete.addEventListener('click', this.promptDelete.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
promptDelete(){
|
||||||
|
var confirm = window.prompt(`Warning: You are about to nuke ${this.channel} off of the face of the fucking planet, no taksie-backsies. \n \n Type in ${this.channel} to confirm.`);
|
||||||
|
this.deleteChannel(confirm);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteChannel(confirm){
|
||||||
|
if(this.channel === confirm){
|
||||||
|
utils.ajax.deleteChannel(this.channel, confirm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelSettings = new channelSettingsPage();
|
||||||
|
|
@ -34,6 +34,64 @@ class canopyUXUtils{
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//We should really move this over to uxutils along with newrow & newtable functions
|
||||||
|
newTableCell(content, firstCol = false){
|
||||||
|
//Create a new 'td' element
|
||||||
|
const cell = document.createElement('td');
|
||||||
|
cell.classList.add("admin-list-entry","admin-list-entry-item");
|
||||||
|
|
||||||
|
//If it's not the first column, mention it!
|
||||||
|
if(!firstCol){
|
||||||
|
cell.classList.add("admin-list-entry-not-first-col");
|
||||||
|
}
|
||||||
|
|
||||||
|
//check for arrays
|
||||||
|
if(content.forEach == null){
|
||||||
|
//add single items
|
||||||
|
addContent(content);
|
||||||
|
}else{
|
||||||
|
//Crawl through content array
|
||||||
|
content.forEach((item)=>{
|
||||||
|
//add each item
|
||||||
|
addContent(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//return the resulting cell
|
||||||
|
return cell;
|
||||||
|
|
||||||
|
|
||||||
|
function addContent(ct){
|
||||||
|
//If we're adding as node
|
||||||
|
if(ct.cloneNode != null){
|
||||||
|
//append it like it's a node
|
||||||
|
cell.appendChild(ct);
|
||||||
|
}else{
|
||||||
|
//otherwise use it as innerHTML
|
||||||
|
cell.innerHTML = ct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
newTableRow(cellContent){
|
||||||
|
//Create an empty table row to hold the cells
|
||||||
|
const entryRow = document.createElement('tr');
|
||||||
|
entryRow.classList.add("admin-list-entry");
|
||||||
|
|
||||||
|
entryRow.appendChild(this.newTableCell(cellContent[0], true));
|
||||||
|
|
||||||
|
cellContent.forEach((content, i)=>{
|
||||||
|
if(i > 0){
|
||||||
|
entryRow.append(this.newTableCell(content));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return entryRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static popup = class{
|
static popup = class{
|
||||||
constructor(content, ajaxPopup = false, cb){
|
constructor(content, ajaxPopup = false, cb){
|
||||||
//Define non-popup node values
|
//Define non-popup node values
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue