Added banlist and fixed pointer stringify
parent
f743f9b3fc
commit
90a042eea3
|
@ -1,4 +1,9 @@
|
|||
# Changelog:
|
||||
* **20.10.18**
|
||||
- Project restructuring (forgot to update the changelog)
|
||||
- Added a complete ban ui
|
||||
- Fixed pointer stringify bug
|
||||
|
||||
* **30.09.18**
|
||||
- Added the permission system (Assignments and management)
|
||||
* Fixed poke and client description with empty message
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
?>
|
||||
|
||||
<link rel="stylesheet" href="css/main.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/helptag.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/scroll.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/ts/tab.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/ts/chat.css" type="text/css">
|
||||
|
@ -47,6 +48,8 @@
|
|||
<link rel="stylesheet" href="css/ts/icons.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/general.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/modals.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/modal-banlist.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/modal-bancreate.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/loader.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/music/info_plate.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/frame/SelectInfo.css" type="text/css">
|
||||
|
@ -93,7 +96,7 @@
|
|||
</div>
|
||||
</head>
|
||||
<body>
|
||||
<?php if(!$CLIENT) { ?>
|
||||
<?php if(!$CLIENT && false) { ?>
|
||||
<!-- No javascript error -->
|
||||
<div style="display: block; position: fixed; top: 0px; bottom: 0px; left: 0px; right: 0px; background-color: gray; z-index: 1000; text-align: center;" class="no-js">
|
||||
<div style="position: relative; display: inline-block; top: 30%">
|
||||
|
@ -128,8 +131,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Critical load error -->
|
||||
<div style="display: none; position: fixed; top: 0; bottom: 0; left: 0; right: 0; background-color: gray; z-index: 1000; text-align: center;"
|
||||
id="critical-load">
|
||||
<div style="display: none; position: fixed; top: 0; bottom: 0; left: 0; right: 0; background-color: gray; z-index: 1000; text-align: center;" id="critical-load">
|
||||
<div style="position: relative; display: inline-block; top: 30%">
|
||||
<img src="img/script.svg" height="128px">
|
||||
<h1 style="color: red">Got some trouble while loading important files!</h1>
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
"sass": "^1.14.1",
|
||||
"typescript": "^3.1.1"
|
||||
},
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"@types/moment": "^2.13.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/TeaSpeak/TeaWeb/TeaWeb.git"
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
tsc -p tsconfig/tsconfig_release.json
|
||||
npm run compile-sass
|
||||
npm run build-worker
|
||||
npm run build-web-app-release
|
||||
npm run build-web-preload
|
||||
#uglifyjs -c --source-map --verbose -o generated/js/client.min.js generated/js/client.js
|
||||
mv generated/js/client.js generated/js/client.min.js
|
||||
cp generated/js/client.js generated/js/client.min.js
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
.help-tip {
|
||||
z-index: 100;
|
||||
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
background-color: #BCDBEA;
|
||||
border-radius: 50%;
|
||||
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 26px;
|
||||
|
||||
cursor: default;
|
||||
|
||||
&:before {
|
||||
content:'?';
|
||||
font-weight: bold;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
p {
|
||||
display:block;
|
||||
transform-origin: 100% 0%;
|
||||
|
||||
-webkit-animation: fadeIn 0.3s ease-in-out;
|
||||
animation: fadeIn 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
display: none;
|
||||
text-align: left;
|
||||
background-color: #1E2021;
|
||||
padding: 20px;
|
||||
|
||||
width: 400px; /* fallback */
|
||||
width: max-content;
|
||||
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2);
|
||||
color: #FFF;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width:0;
|
||||
height: 0;
|
||||
border:6px solid transparent;
|
||||
border-bottom-color:#1E2021;
|
||||
top:-12px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
width:100%;
|
||||
height:40px;
|
||||
content:'';
|
||||
position: absolute;
|
||||
top:-40px;
|
||||
left:0;
|
||||
}
|
||||
}
|
||||
|
||||
&.tip-left {
|
||||
p {
|
||||
right: -4px;
|
||||
|
||||
&:before {
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tip-right {
|
||||
p {
|
||||
left: -4px;
|
||||
|
||||
&:before {
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.tip-center {
|
||||
p {
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
|
||||
&:before {
|
||||
right: calc(50% - 5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.tip-small {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeIn {
|
||||
0% {
|
||||
opacity:0;
|
||||
transform: scale(0.6);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity:100%;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% { opacity:0; }
|
||||
100% { opacity:100%; }
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
.bancreate {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.frame-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&.container-reason {
|
||||
max-height: 500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container-name-type {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-top: 2px;
|
||||
|
||||
* {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.container-time-input {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
.container-global {
|
||||
display: inline-block;
|
||||
|
||||
.input-global {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
.container-buttons {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
.banlist {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
flex-grow: 1;
|
||||
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.frame-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
flex-grow: 1;
|
||||
|
||||
.top-menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.manage-buttons {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.search {
|
||||
flex-grow: 1;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.entry-container {
|
||||
display: inline-flex;
|
||||
flex-grow: 1;
|
||||
margin-bottom: 5px;
|
||||
position: relative;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.table-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ban-entry.selected {
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.ban-entry-global {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.ban-entry {
|
||||
.field-properties {
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.field-reason {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
&.ban-entry-own-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
border: grey solid 1px;
|
||||
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: left;
|
||||
min-width: 610px;
|
||||
|
||||
tr {
|
||||
td, th {
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
|
||||
&:nth-child(1) {
|
||||
width: 25%;
|
||||
text-align: left;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
width: 25%;
|
||||
float: left;
|
||||
}
|
||||
&:nth-child(3) {
|
||||
width: 25%;
|
||||
float: left;
|
||||
}
|
||||
&:nth-child(4) {
|
||||
width: 25%;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
border: grey solid;
|
||||
border-width: 0 0 1px 1px;
|
||||
|
||||
&:first-of-type {
|
||||
border-width: 0 0 1px 0;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
height: calc(100% - 22px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
th, td, thead,tbody {
|
||||
display: block;
|
||||
}
|
||||
tr {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
&:nth-of-type(even) {
|
||||
background-color: lightgray;
|
||||
}
|
||||
}
|
||||
td,th {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
div {
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -87,10 +87,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
/* Channel edit/create modal */
|
||||
.settings_audio {
|
||||
display: grid;
|
||||
|
@ -337,6 +333,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.container-ban-type {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: inline-block;
|
||||
border: solid black;
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace TSIdentityHelper {
|
|||
let functionDestroyString: any;
|
||||
let functionDestroyIdentity: any;
|
||||
|
||||
export function setup() : boolean {
|
||||
export function setup() : boolean {
|
||||
functionDestroyString = Module.cwrap("destroy_string", "pointer", []);
|
||||
functionLastError = Module.cwrap("last_error_message", null, ["string"]);
|
||||
funcationParseIdentity = Module.cwrap("parse_identity", "pointer", ["string"]);
|
||||
|
@ -43,10 +43,10 @@ namespace TSIdentityHelper {
|
|||
export function unwarpString(str) : string {
|
||||
if(str == "") return "";
|
||||
try {
|
||||
if(!$.isFunction(window.Pointer_stringify) || !$.isFunction(Pointer_stringify)) {
|
||||
if(!$.isFunction(window.Pointer_stringify)) {
|
||||
displayCriticalError("Missing required wasm function!<br>Please reload the page!");
|
||||
}
|
||||
let message: string = Pointer_stringify(str);
|
||||
let message: string = window.Pointer_stringify(str);
|
||||
functionDestroyString(str);
|
||||
return message;
|
||||
} catch (error) {
|
||||
|
|
|
@ -215,7 +215,7 @@ class ServerConnection {
|
|||
"type": "command",
|
||||
"command": command,
|
||||
"data": _data,
|
||||
"flags": flags
|
||||
"flags": flags.filter(entry => entry.length != 0)
|
||||
}));
|
||||
});
|
||||
return new Promise<CommandResult>((resolve, failed) => {
|
||||
|
@ -225,7 +225,8 @@ class ServerConnection {
|
|||
let res = ex;
|
||||
if(!res.success) {
|
||||
if(res.id == 2568) { //Permission error
|
||||
chat.serverChat().appendError("insufficient client permissions. Failed on permission {}", this._client.permissions.resolveInfo(res.json["failed_permid"] as number).name);
|
||||
res.message = "Insufficient client permissions. Failed on permission " + this._client.permissions.resolveInfo(res.json["failed_permid"] as number).name;
|
||||
chat.serverChat().appendError("Insufficient client permissions. Failed on permission {}", this._client.permissions.resolveInfo(res.json["failed_permid"] as number).name);
|
||||
} else {
|
||||
chat.serverChat().appendError(res.extra_message.length == 0 ? res.message : res.extra_message);
|
||||
}
|
||||
|
|
|
@ -136,6 +136,8 @@ function loadDebug() {
|
|||
"js/ui/modal/ModalConnect.js",
|
||||
"js/ui/modal/ModalChangeVolume.js",
|
||||
"js/ui/modal/ModalBanClient.js",
|
||||
"js/ui/modal/ModalBanCreate.js",
|
||||
"js/ui/modal/ModalBanList.js",
|
||||
"js/ui/modal/ModalYesNo.js",
|
||||
"js/ui/modal/ModalPermissionEdit.js",
|
||||
"js/ui/modal/ModalServerGroupDialog.js",
|
||||
|
@ -224,6 +226,10 @@ function loadTemplates() {
|
|||
tags = node.children;
|
||||
|
||||
let root = document.getElementById("templates");
|
||||
if(!root) {
|
||||
displayCriticalError("Failed to find template tag!");
|
||||
return;
|
||||
}
|
||||
while(tags.length > 0){
|
||||
let tag = tags.item(0);
|
||||
if(tag.id == "tmpl_main") {
|
||||
|
@ -283,6 +289,7 @@ function loadSide() {
|
|||
"vendor/jsrender/jsrender.min.js"
|
||||
])).then(() => load_wait_scripts([
|
||||
["vendor/bbcode/xbbcode.js"],
|
||||
["vendor/moment/moment.js"],
|
||||
["https://webrtc.github.io/adapter/adapter-latest.js"]
|
||||
])).then(() => {
|
||||
//Load the teaweb scripts
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
/// <reference path="utils/modal.ts" />
|
||||
/// <reference path="ui/modal/ModalConnect.ts" />
|
||||
/// <reference path="ui/modal/ModalCreateChannel.ts" />
|
||||
/// <reference path="ui/modal/ModalBanCreate.ts" />
|
||||
/// <reference path="ui/modal/ModalBanClient.ts" />
|
||||
/// <reference path="ui/modal/ModalYesNo.ts" />
|
||||
/// <reference path="ui/modal/ModalBanList.ts" />
|
||||
/// <reference path="codec/CodecWrapper.ts" />
|
||||
/// <reference path="settings.ts" />
|
||||
/// <reference path="log.ts" />
|
||||
|
@ -49,6 +51,7 @@ function setup_close() {
|
|||
};
|
||||
}
|
||||
|
||||
declare function moment(...arguments) : any;
|
||||
function setup_jsrender() : boolean {
|
||||
if(!js_render) {
|
||||
displayCriticalError("Missing jsrender extension!");
|
||||
|
@ -66,6 +69,10 @@ function setup_jsrender() : boolean {
|
|||
return (Math.round(Math.random() * (min + max + 1) - min)).toString();
|
||||
});
|
||||
|
||||
js_render.views.tags("fmt_date", (...arguments) => {
|
||||
return moment(arguments[0]).format(arguments[1]);
|
||||
});
|
||||
|
||||
$(".jsrender-template").each((idx, _entry) => {
|
||||
if(!js_render.templates(_entry.id, _entry.innerHTML)) { //, _entry.innerHTML
|
||||
console.error("Failed to cache template " + _entry.id + " for js render!");
|
||||
|
@ -138,8 +145,6 @@ function main() {
|
|||
})
|
||||
*/
|
||||
|
||||
//Modals.spawnPermissionEdit();
|
||||
|
||||
setup_close();
|
||||
$(window).on('resize', () => {
|
||||
globalClient.channelTree.handle_resized();
|
||||
|
@ -154,7 +159,7 @@ app.loadedListener.push(() => {
|
|||
$(document).one('click', event => AudioController.initializeFromGesture());
|
||||
}
|
||||
} catch (ex) {
|
||||
if(ex instanceof ReferenceError)
|
||||
if(ex instanceof ReferenceError || ex instanceof TypeError)
|
||||
ex = ex.message + ":<br>" + ex.stack;
|
||||
displayCriticalError("Failed to invoke main function:<br>" + ex);
|
||||
}
|
||||
|
|
|
@ -307,12 +307,12 @@ class ClientEntry {
|
|||
name: "Ban client",
|
||||
invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
|
||||
callback: () => {
|
||||
Modals.spawnBanClient(this.properties.client_nickname, (duration, reason) => {
|
||||
Modals.spawnBanClient(this.properties.client_nickname, (data) => {
|
||||
this.channelTree.client.serverConnection.sendCommand("banclient", {
|
||||
uid: this.properties.client_unique_identifier,
|
||||
banreason: reason,
|
||||
time: duration
|
||||
});
|
||||
banreason: data.reason,
|
||||
time: data.length
|
||||
}, [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""]);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/// <reference path="../../client.ts" />
|
||||
/// <reference path="../modal/ModalSettings.ts" />
|
||||
/// <reference path="../modal/ModalBanList.ts" />
|
||||
/*
|
||||
client_output_hardware Value: '1'
|
||||
client_output_muted Value: '0'
|
||||
|
@ -11,6 +12,8 @@
|
|||
client_away Value: '0'
|
||||
client_away_message Value: ''
|
||||
*/
|
||||
import openBanList = Modals.openBanList;
|
||||
|
||||
class ControlBar {
|
||||
private _muteInput: boolean;
|
||||
private _muteOutput: boolean;
|
||||
|
@ -36,6 +39,7 @@ class ControlBar {
|
|||
this.htmlTag.find(".btn_mute_output").on('click', this.onOutputMute.bind(this));
|
||||
this.htmlTag.find(".btn_open_settings").on('click', this.onOpenSettings.bind(this));
|
||||
this.htmlTag.find(".btn_permissions").on('click', this.onPermission.bind(this));
|
||||
this.htmlTag.find(".btn_banlist").on('click', this.onBanlist.bind(this));
|
||||
{
|
||||
let tokens = this.htmlTag.find(".btn_token");
|
||||
tokens.find(".button-dropdown").on('click', () => {
|
||||
|
@ -256,4 +260,10 @@ class ControlBar {
|
|||
button.removeClass("activated");
|
||||
}, 0);
|
||||
}
|
||||
|
||||
private onBanlist() {
|
||||
if(!this.handle.serverConnection) return;
|
||||
|
||||
openBanList(this.handle);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,13 @@
|
|||
/// <reference path="../../client.ts" />
|
||||
|
||||
namespace Modals {
|
||||
export function spawnBanClient(name: string, callback: (length: number, reason: string) => void) {
|
||||
export function spawnBanClient(name: string, callback: (data: {
|
||||
length: number,
|
||||
reason: string,
|
||||
no_name: boolean,
|
||||
no_ip: boolean,
|
||||
no_hwid: boolean
|
||||
}) => void) {
|
||||
const connectModal = createModal({
|
||||
header: function() {
|
||||
return "Ban client";
|
||||
|
@ -85,7 +91,14 @@ namespace Modals {
|
|||
let duration = connectModal.htmlTag.find(".ban_duration_type option:selected");
|
||||
console.log(duration);
|
||||
console.log(length + "*" + duration.attr("duration-scale"));
|
||||
callback(length * parseInt(duration.attr("duration-scale")), connectModal.htmlTag.find(".ban_reason").val() as string);
|
||||
|
||||
callback({
|
||||
length: length * parseInt(duration.attr("duration-scale")),
|
||||
reason: connectModal.htmlTag.find(".ban_reason").val() as string,
|
||||
no_hwid: !connectModal.htmlTag.find(".ban-type-hardware-id").prop("checked"),
|
||||
no_ip: !connectModal.htmlTag.find(".ban-type-ip").prop("checked"),
|
||||
no_name: !connectModal.htmlTag.find(".ban-type-nickname").prop("checked")
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
namespace Modals {
|
||||
export function spawnBanCreate(base?: BanEntry, callback?: (entry?: BanEntry) => any) {
|
||||
let result: BanEntry = {} as any;
|
||||
result.banid = base ? base.banid : 0;
|
||||
|
||||
let modal: Modal;
|
||||
modal = createModal({
|
||||
header: base && base.banid > 0 ? "Edit ban" : "Add ban",
|
||||
body: () => {
|
||||
let template = $("#tmpl_ban_create").renderTag();
|
||||
template = $.spawn("div").append(template);
|
||||
|
||||
const input_name = template.find(".input-name");
|
||||
const input_name_type = template.find(".input-name-type");
|
||||
const input_ip = template.find(".input-ip");
|
||||
const input_uid = template.find(".input-uid");
|
||||
const input_reason = template.find(".input-reason");
|
||||
const input_time = template.find(".input-time");
|
||||
const input_time_type = template.find(".input-time-unit");
|
||||
const input_hwid = template.find(".input-hwid");
|
||||
const input_global = template.find(".input-global");
|
||||
|
||||
{
|
||||
let maxTime = 0; //globalClient.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value;
|
||||
let unlimited = maxTime == 0 || maxTime == -1;
|
||||
if(unlimited) maxTime = 0;
|
||||
|
||||
input_time_type.find("option[value=\"sec\"]").prop("disabled", !unlimited && 1 > maxTime)
|
||||
.attr("duration-scale", 1)
|
||||
.attr("duration-max", maxTime);
|
||||
input_time_type.find("option[value=\"min\"]").prop("disabled", !unlimited && 60 > maxTime)
|
||||
.attr("duration-scale", 60)
|
||||
.attr("duration-max", maxTime / 60);
|
||||
input_time_type.find("option[value=\"hours\"]").prop("disabled", !unlimited && 60 * 60 > maxTime)
|
||||
.attr("duration-scale", 60 * 60)
|
||||
.attr("duration-max", maxTime / (60 * 60));
|
||||
input_time_type.find("option[value=\"days\"]").prop("disabled", !unlimited && 60 * 60 * 24 > maxTime)
|
||||
.attr("duration-scale", 60 * 60 * 24)
|
||||
.attr("duration-max", maxTime / (60 * 60 * 24));
|
||||
input_time_type.find("option[value=\"perm\"]").prop("disabled", !unlimited)
|
||||
.attr("duration-scale", 0);
|
||||
|
||||
input_time_type.change(event => {
|
||||
let element = $((event.target as HTMLSelectElement).selectedOptions.item(0));
|
||||
if(element.val() !== "perm") {
|
||||
input_time.prop("disabled", false);
|
||||
|
||||
let current = input_time.val() as number;
|
||||
let max = parseInt(element.attr("duration-max"));
|
||||
if (max > 0 && current > max)
|
||||
input_time.val(max);
|
||||
else if(current <= 0)
|
||||
input_time.val(1);
|
||||
input_time.attr("max", max);
|
||||
} else {
|
||||
input_time.prop("disabled", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template.find('input, textarea').on('keyup change', event => {
|
||||
let valid = false;
|
||||
|
||||
if(input_name.val() || input_ip.val() || input_uid.val())
|
||||
valid = true;
|
||||
|
||||
modal.htmlTag.find(".button-success").prop("disabled", !valid);
|
||||
});
|
||||
|
||||
if(base) {
|
||||
input_ip.val(base.ip);
|
||||
input_uid.val(base.unique_id);
|
||||
input_name.val(base.name);
|
||||
input_hwid.val(base.hardware_id);
|
||||
|
||||
(input_name_type[0] as HTMLSelectElement).selectedIndex = base.name_type || 0;
|
||||
input_reason.val(base.reason);
|
||||
|
||||
if(base.timestamp_expire.getTime() == 0) {
|
||||
input_time_type.find("option[value=\"perm\"]").prop("selected", true);
|
||||
} else {
|
||||
const time = (base.timestamp_expire.getTime() - base.timestamp_created.getTime()) / 1000;
|
||||
if(time % (60 * 60 * 24) === 0) {
|
||||
input_time_type.find("option[value=\"days\"]").prop("selected", true);
|
||||
input_time.val(time / (60 * 60 * 24));
|
||||
} else if(time % (60 * 60) === 0) {
|
||||
input_time_type.find("option[value=\"hours\"]").prop("selected", true);
|
||||
input_time.val(time / (60 * 60));
|
||||
} else if(time % (60) === 0) {
|
||||
input_time_type.find("option[value=\"min\"]").prop("selected", true);
|
||||
input_time.val(time / (60));
|
||||
} else {
|
||||
input_time_type.find("option[value=\"sec\"]").prop("selected", true);
|
||||
input_time.val(time);
|
||||
}
|
||||
}
|
||||
|
||||
template.find(".container-global").detach(); //We cant edit this
|
||||
input_global.prop("checked", base.server_id == 0);
|
||||
}
|
||||
|
||||
if(globalClient && globalClient.permissions)
|
||||
input_global.prop("disabled", !globalClient.permissions.neededPermission(base ? PermissionType.B_CLIENT_BAN_EDIT_GLOBAL : PermissionType.B_CLIENT_BAN_CREATE_GLOBAL));
|
||||
|
||||
return template;
|
||||
},
|
||||
footer: undefined
|
||||
});
|
||||
|
||||
modal.htmlTag.find(".button-close").on('click', () => modal.close());
|
||||
modal.htmlTag.find(".button-success").on('click', () => {
|
||||
{
|
||||
let length = modal.htmlTag.find(".input-time").val() as number;
|
||||
let duration = modal.htmlTag.find(".input-time-unit option:selected");
|
||||
|
||||
console.log(duration);
|
||||
console.log(length + "*" + duration.attr("duration-scale"));
|
||||
const time = length * parseInt(duration.attr("duration-scale"));
|
||||
|
||||
if(!result.timestamp_created)
|
||||
result.timestamp_created = new Date();
|
||||
if(time > 0)
|
||||
result.timestamp_expire = new Date(result.timestamp_created.getTime() + time * 1000);
|
||||
else
|
||||
result.timestamp_expire = new Date(0);
|
||||
}
|
||||
{
|
||||
result.name = modal.htmlTag.find(".input-name").val() as string;
|
||||
{
|
||||
const name_type = modal.htmlTag.find(".input-name-type") as JQuery<HTMLSelectElement>;
|
||||
result.name_type = name_type[0].selectedIndex;
|
||||
}
|
||||
result.ip = modal.htmlTag.find(".input-ip").val() as string;
|
||||
result.unique_id = modal.htmlTag.find(".input-uid").val() as string;
|
||||
result.reason = modal.htmlTag.find(".input-reason").val() as string;
|
||||
result.hardware_id = modal.htmlTag.find(".input-hwid").val() as string;
|
||||
result.server_id = modal.htmlTag.find(".input-global").prop("checked") ? 0 : -1;
|
||||
}
|
||||
|
||||
modal.close();
|
||||
if(callback) callback(result);
|
||||
});
|
||||
|
||||
modal.htmlTag.find("input").trigger("change");
|
||||
|
||||
modal.open();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
namespace Modals {
|
||||
export interface BanEntry {
|
||||
server_id: number;
|
||||
banid: number;
|
||||
|
||||
name?: string;
|
||||
name_type?: number;
|
||||
|
||||
unique_id?: string;
|
||||
ip?: string;
|
||||
hardware_id?: string;
|
||||
|
||||
reason: string;
|
||||
invoker_name: string;
|
||||
invoker_unique_id?: string;
|
||||
invoker_database_id?: number;
|
||||
|
||||
timestamp_created: Date;
|
||||
timestamp_expire: Date;
|
||||
|
||||
enforcements: number;
|
||||
|
||||
flag_own?: boolean;
|
||||
}
|
||||
export interface BanListManager {
|
||||
addbans : (ban: BanEntry[]) => void;
|
||||
clear : (ban?: any) => void;
|
||||
}
|
||||
|
||||
export function openBanList(client: TSClient) {
|
||||
let update;
|
||||
const modal = spawnBanListModal(() => update(), () => {
|
||||
spawnBanCreate(undefined, result => {
|
||||
if(result.server_id < 0) result.server_id = undefined;
|
||||
console.log("Adding ban %o", result);
|
||||
|
||||
client.serverConnection.sendCommand("banadd", {
|
||||
ip: result.ip,
|
||||
name: result.name,
|
||||
uid: result.unique_id,
|
||||
hwid: result.hardware_id,
|
||||
banreason: result.reason,
|
||||
time: result.timestamp_expire.getTime() > 0 ? (result.timestamp_expire.getTime() - result.timestamp_created.getTime()) / 1000 : 0,
|
||||
sid: result.server_id
|
||||
}).then(() => {
|
||||
update();
|
||||
}).catch(error => {
|
||||
createErrorModal("Failed to add ban", "Failed to add ban.<br>Reason: " + (error instanceof CommandResult ? error.extra_message || error.message : error)).open();
|
||||
});
|
||||
});
|
||||
}, ban => {
|
||||
console.log("Editing ban %o", ban);
|
||||
spawnBanCreate(ban, result => {
|
||||
console.log("Apply edit changes %o", result);
|
||||
if(result.server_id < 0) result.server_id = undefined;
|
||||
|
||||
client.serverConnection.sendCommand("banedit", {
|
||||
banid: result.banid,
|
||||
ip: result.ip,
|
||||
name: result.name,
|
||||
uid: result.unique_id,
|
||||
hwid: result.hardware_id,
|
||||
banreason: result.reason,
|
||||
time: result.timestamp_expire.getTime() > 0 ? (result.timestamp_expire.getTime() - result.timestamp_created.getTime()) / 1000 : 0,
|
||||
sid: result.server_id
|
||||
}).then(() => {
|
||||
update();
|
||||
}).catch(error => {
|
||||
createErrorModal("Failed to edit ban", "Failed to edit ban.<br>Reason: " + (error instanceof CommandResult ? error.extra_message || error.message : error)).open();
|
||||
});
|
||||
});
|
||||
}, ban => {
|
||||
console.log("Deleting ban %o", ban);
|
||||
client.serverConnection.sendCommand("bandel", {
|
||||
banid: ban.banid,
|
||||
sid: ban.server_id
|
||||
}).then(() => {
|
||||
update();
|
||||
}).catch(error => {
|
||||
createErrorModal("Failed to delete ban", "Failed to delete ban.<br>Reason: " + (error instanceof CommandResult ? error.extra_message || error.message : error)).open();
|
||||
});
|
||||
});
|
||||
|
||||
update = () => {
|
||||
client.serverConnection.commandHandler["notifybanlist"] = json => {
|
||||
console.log("Got banlist: ", json);
|
||||
|
||||
let bans: BanEntry[] = [];
|
||||
for(const entry of json) {
|
||||
/*
|
||||
notify[index]["sid"] = elm->serverId;
|
||||
notify[index]["banid"] = elm->banId;
|
||||
if(allow_ip)
|
||||
notify[index]["ip"] = elm->ip;
|
||||
else
|
||||
notify[index]["ip"] = "hidden";
|
||||
notify[index]["name"] = elm->name;
|
||||
notify[index]["uid"] = elm->uid;
|
||||
notify[index]["lastnickname"] = elm->name; //Maybe update?
|
||||
|
||||
notify[index]["created"] = chrono::duration_cast<chrono::seconds>(elm->created.time_since_epoch()).count();
|
||||
if (elm->until.time_since_epoch().count() != 0)
|
||||
notify[index]["duration"] = chrono::duration_cast<chrono::seconds>(elm->until - elm->created).count();
|
||||
else
|
||||
notify[index]["duration"] = 0;
|
||||
|
||||
notify[index]["reason"] = elm->reason;
|
||||
notify[index]["enforcements"] = elm->triggered;
|
||||
|
||||
notify[index]["invokername"] = elm->invokerName;
|
||||
notify[index]["invokercldbid"] = elm->invokerDbId;
|
||||
notify[index]["invokeruid"] = elm->invokerUid;
|
||||
*/
|
||||
bans.push({
|
||||
server_id: parseInt(entry["sid"]),
|
||||
banid: parseInt(entry["banid"]),
|
||||
ip: entry["ip"],
|
||||
name: entry["name"],
|
||||
unique_id: entry["uid"],
|
||||
hardware_id: entry["hwid"],
|
||||
|
||||
timestamp_created: new Date(parseInt(entry["created"]) * 1000),
|
||||
timestamp_expire: new Date(parseInt(entry["duration"]) > 0 ? parseInt(entry["created"]) * 1000 + parseInt(entry["duration"]) * 1000 : 0),
|
||||
|
||||
invoker_name: entry["invokername"],
|
||||
invoker_database_id: parseInt(entry["invokercldbid"]),
|
||||
invoker_unique_id: entry["invokeruid"],
|
||||
reason: entry["reason"],
|
||||
|
||||
enforcements: parseInt(entry["enforcements"]),
|
||||
flag_own: entry["invokeruid"] == client.getClient().properties.client_unique_identifier
|
||||
});
|
||||
}
|
||||
|
||||
modal.addbans(bans);
|
||||
};
|
||||
|
||||
//TODO test permission
|
||||
modal.clear();
|
||||
client.serverConnection.sendCommand("banlist", { sid: 0 }); //Global ban list
|
||||
client.serverConnection.sendCommand("banlist").catch(error => {
|
||||
if(error instanceof CommandResult) {
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
export function spawnBanListModal(callback_update: () => any, callback_add: () => any, callback_edit: (entry: BanEntry) => any, callback_delete: (entry: BanEntry) => any) : BanListManager {
|
||||
let result: BanListManager = {} as any;
|
||||
|
||||
let entries: BanEntry[] = [];
|
||||
const _callback_edit = ban_id => {
|
||||
for(const entry of entries)
|
||||
if(entry.banid == ban_id) {
|
||||
callback_edit(entry);
|
||||
return;
|
||||
}
|
||||
console.warn("Missing ban entry with id " + ban_id);
|
||||
};
|
||||
|
||||
const _callback_delete = ban_id => {
|
||||
for (const entry of entries)
|
||||
if (entry.banid == ban_id) {
|
||||
callback_delete(entry);
|
||||
return;
|
||||
}
|
||||
console.warn("Missing ban entry with id " + ban_id);
|
||||
};
|
||||
|
||||
let update_function: () => any;
|
||||
let modal: Modal;
|
||||
modal = createModal({
|
||||
header: "Banlist",
|
||||
body: () => {
|
||||
let template = $("#tmpl_ban_list").renderTag();
|
||||
template = $.spawn("div").append(template);
|
||||
|
||||
apply_filter(template.find(".entry-filter"), template.find(".filter-flag-force-own"), template.find(".filter-flag-highlight-own"), template.find(".ban-entry-list"));
|
||||
update_function = apply_buttons(template.find(".manage-buttons"), template.find(".ban-entry-list"), callback_add, _callback_edit, _callback_delete);
|
||||
template.find(".button-close").on('click', _ => modal.close());
|
||||
template.find(".button-refresh").on('click', () => callback_update());
|
||||
return template;
|
||||
},
|
||||
footer: undefined,
|
||||
width: "80%",
|
||||
height: "80%"
|
||||
});
|
||||
modal.open();
|
||||
modal.close_listener.push(() => entries = []);
|
||||
|
||||
result.addbans = (bans: BanEntry[]) => {
|
||||
for(const entry of bans) {
|
||||
entries.push(entry);
|
||||
$("#tmpl_ban_entry").renderTag(entry).appendTo(modal.htmlTag.find(".ban-entry-list"));
|
||||
}
|
||||
modal.htmlTag.find(".entry-filter").trigger("change");
|
||||
update_function();
|
||||
};
|
||||
result.clear = () => {
|
||||
entries = [];
|
||||
modal.htmlTag.find(".ban-entry-list").children().detach();
|
||||
update_function();
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function apply_filter(input: JQuery, show_own_bans: JQuery, highlight_own_bans: JQuery, elements: JQuery) {
|
||||
input.on('keyup change', event => {
|
||||
const filter = (input.val() as string).trim();
|
||||
const show_own_only = show_own_bans.prop("checked");
|
||||
const highlight_own = highlight_own_bans.prop("checked");
|
||||
|
||||
console.log("Search for filter %s", filter);
|
||||
|
||||
let shown = 0, hidden = 0;
|
||||
elements.find(".ban-entry").each((_idx, _entry) => {
|
||||
const entry = $(_entry);
|
||||
if(entry.hasClass("ban-entry-own")) {
|
||||
if(highlight_own)
|
||||
entry.addClass("ban-entry-own-bold");
|
||||
else
|
||||
entry.removeClass("ban-entry-own-bold");
|
||||
|
||||
} else if(show_own_only) {
|
||||
if(entry.hide().hasClass("selected"))
|
||||
entry.trigger("click");
|
||||
hidden++;
|
||||
return;
|
||||
}
|
||||
if(filter.length == 0 || entry.text().indexOf(filter) > 0) {
|
||||
entry.show();
|
||||
shown++;
|
||||
} else {
|
||||
if(entry.hide().hasClass("selected"))
|
||||
entry.trigger("click");
|
||||
hidden++;
|
||||
}
|
||||
});
|
||||
|
||||
$(".entry-count-info").text((shown + hidden) + " entries. " + shown + " entries shown");
|
||||
});
|
||||
show_own_bans.on('click', () => input.trigger('change'));
|
||||
highlight_own_bans.on('click', () => input.trigger('change'));
|
||||
}
|
||||
|
||||
function apply_buttons(tag: JQuery, elements: JQuery, cb_add: () => any, cb_edit: (id: number) => any, cb_delete: (id: number) => any) : () => any {
|
||||
const update = () => {
|
||||
console.log(elements.find("tr.selected").length);
|
||||
$(".button-edit, .button-remove").prop("disabled", elements.find("tr.selected").length == 0);
|
||||
};
|
||||
|
||||
tag.find(".button-add").on('click', event => cb_add());
|
||||
tag.find(".button-edit").on('click', event => {
|
||||
const selected = elements.find("tr.selected");
|
||||
if(!selected) return;
|
||||
cb_edit(parseInt(selected.attr("ban-id")));
|
||||
});
|
||||
tag.find(".button-remove").on('click', event => {
|
||||
const selected = elements.find("tr.selected");
|
||||
if(!selected) return;
|
||||
cb_delete(parseInt(selected.attr("ban-id")));
|
||||
});
|
||||
|
||||
const element_selected = element => {
|
||||
elements.find("tr").removeClass("selected");
|
||||
if(element.is(":visible"))
|
||||
element.addClass("selected");
|
||||
|
||||
update();
|
||||
};
|
||||
|
||||
const click_handler = event => element_selected($(event.currentTarget));
|
||||
const context_handler = event => {
|
||||
const element = $(event.currentTarget);
|
||||
element_selected(element);
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
spawn_context_menu(event.pageX, event.pageY, {
|
||||
name: "Edit",
|
||||
type: MenuEntryType.ENTRY,
|
||||
callback: () => cb_edit(parseInt(element.attr("ban-id")))
|
||||
}, {
|
||||
name: "Delete",
|
||||
type: MenuEntryType.ENTRY,
|
||||
callback: () => cb_delete(parseInt(element.attr("ban-id")))
|
||||
});
|
||||
};
|
||||
|
||||
return () => {
|
||||
elements.find("tr").each((_idx, _entry) => {
|
||||
_entry.addEventListener("click", click_handler);
|
||||
_entry.addEventListener("contextmenu", context_handler)
|
||||
});
|
||||
update();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
<tr class="ban-entry">
|
||||
<td class="field-properties">
|
||||
<a class="property property-name">name=WolverasdasdinDEV</a>
|
||||
<a class="property property-ip">ip=WolverasdasdinDEV</a>
|
||||
<a class="property property-unique-id">uid=WolverasdasdinDEV</a>
|
||||
<a class="property property-hardware-id">hwid=WolverasdasdinDEV</a>
|
||||
</td>
|
||||
<td class="field-reason">Insult</td>
|
||||
<td class="field-invoker">WolverinDEV</td>
|
||||
<td class="field-timestamp">
|
||||
<a class="timestamp-created">Created: 1.1.2013 12:22</a>
|
||||
<a class="timestamp-expires">Expires: 1.21.2013 12:22</a>
|
||||
</td>
|
||||
</tr>
|
||||
*/
|
|
@ -71,12 +71,16 @@ class ModalProperties {
|
|||
}
|
||||
}
|
||||
|
||||
class Modal {
|
||||
class Modal extends EventTarget {
|
||||
private _htmlTag: JQuery;
|
||||
properties: ModalProperties;
|
||||
shown: boolean;
|
||||
|
||||
close_listener: (() => any)[] = [];
|
||||
|
||||
constructor(props: ModalProperties) {
|
||||
super();
|
||||
|
||||
this.properties = props;
|
||||
this.shown = false;
|
||||
}
|
||||
|
@ -124,12 +128,16 @@ class Modal {
|
|||
}
|
||||
|
||||
close() {
|
||||
if(!this.shown) return;
|
||||
|
||||
this.shown = false;
|
||||
const _this = this;
|
||||
this.htmlTag.animate({opacity: 0}, () => {
|
||||
_this.htmlTag.detach();
|
||||
});
|
||||
this.properties.triggerClose();
|
||||
for(const listener of this.close_listener)
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +231,7 @@ function createErrorModal(header: BodyCreator, message: BodyCreator, props: Moda
|
|||
ModalFunctions.divify(ModalFunctions.jqueriefy(header)).appendTo(head);
|
||||
props.header = head;
|
||||
|
||||
props.body = ModalFunctions.divify(ModalFunctions.jqueriefy(message));
|
||||
props.body = $.spawn("div").append(ModalFunctions.divify(ModalFunctions.jqueriefy(message)));
|
||||
props.footer = ModalFunctions.divify(ModalFunctions.jqueriefy(""));
|
||||
|
||||
return createModal(props);
|
||||
|
|
244
templates.html
244
templates.html
|
@ -56,6 +56,9 @@
|
|||
</div>
|
||||
|
||||
<div style="width: 100%"></div>
|
||||
<div class="button btn_banlist" title="Banlist">
|
||||
<div class="icon_x32 client-ban_list"></div>
|
||||
</div>
|
||||
<div class="button btn_permissions" title="View/edit permissions">
|
||||
<div class="icon_x32 client-permission_overview"></div>
|
||||
</div>
|
||||
|
@ -153,7 +156,7 @@
|
|||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Template for chennel create & edit-->
|
||||
<!-- Template for channel create & edit-->
|
||||
<script class="jsrender-template" id="tmpl_channel_edit" type="text/html">
|
||||
<div class="align_column general_properties">
|
||||
<div class="properties">
|
||||
|
@ -613,31 +616,6 @@
|
|||
<div class="display_volume" style="width: 60px; align-self: center; text-align: center">±0 %</div>
|
||||
</div>
|
||||
</script>
|
||||
<script class="jsrender-template" id="tmpl_client_ban" type="text/html">
|
||||
<div class="align_column">
|
||||
<div class="align_column" style="margin: 5px">
|
||||
<a>Name:</a>
|
||||
<input value="{{>client_name}}" readonly>
|
||||
</div>
|
||||
<div class="align_column" style="margin: 5px">
|
||||
<a>Reason:</a>
|
||||
<textarea style="height: 32px; resize: vertical; max-height: 150px; min-height: 32px" maxlength="512" class="ban_reason"></textarea>
|
||||
</div>
|
||||
<div class="align_row" style="margin: 5px; justify-content: space-between">
|
||||
<a>Duration:</a>
|
||||
<div class="align_row">
|
||||
<input type="number" value="1" class="ban_duration" style="margin-right: 7px" min="1">
|
||||
<select class="ban_duration_type">
|
||||
<option value="sec">seconds</option>
|
||||
<option value="min">minutes</option>
|
||||
<option value="hours">hours</option>
|
||||
<option value="days">days</option>
|
||||
<option value="perm">permanent</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Permission overview -->
|
||||
<script class="jsrender-template" id="tmpl_server_permissions" type="text/html">
|
||||
|
@ -856,6 +834,220 @@
|
|||
</div>
|
||||
</script>
|
||||
|
||||
<!-- General management templates -->
|
||||
<script class="jsrender-template" id="tmpl_ban_list" type="text/html">
|
||||
<div class="banlist">
|
||||
<div class="frame-container">
|
||||
<div class="top-menu">
|
||||
<div class="manage-buttons">
|
||||
<button class="button-add">Add</button>
|
||||
<button class="button-edit">Edit</button>
|
||||
<button class="button-remove">Remove</button>
|
||||
</div>
|
||||
<div class="search">
|
||||
<input type="text" class="entry-filter">
|
||||
</div>
|
||||
</div>
|
||||
<div class="entry-container">
|
||||
<div class="table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="field-properties">Name/IP/UID/HWID</th>
|
||||
<th class="field-reason">Reason</th>
|
||||
<th class="field-invoker">Creator</th>
|
||||
<th class="field-timestamp">Created / Expires</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="ban-entry-list">
|
||||
<!--
|
||||
<tr class="ban-entry">
|
||||
<td class="field-properties">
|
||||
<a class="property property-name">name=WolverasdasdinDEV</a>
|
||||
<a class="property property-ip">ip=WolverasdasdinDEV</a>
|
||||
<a class="property property-unique-id">uid=WolverasdasdinDEV</a>
|
||||
<a class="property property-hardware-id">hwid=WolverasdasdinDEV</a>
|
||||
</td>
|
||||
<td class="field-reason">Insult</td>
|
||||
<td class="field-invoker">WolverinDEV</td>
|
||||
<td class="field-timestamp">1.1.2013 12:22 / 1.21.2013 12:22</td>
|
||||
</tr>
|
||||
<tr class="ban-entry">
|
||||
<td class="field-properties">
|
||||
<a class="property property-name">name=WolverasdasdinDEV</a>
|
||||
<a class="property property-ip">ip=WolverasdasdinDEV</a>
|
||||
<a class="property property-unique-id">uid=WolverasdasdinDEV</a>
|
||||
<a class="property property-hardware-id">hwid=WolverasdasdinDEV</a>
|
||||
</td>
|
||||
<td class="field-reason">Insult</td>
|
||||
<td class="field-invoker">WolverinDEV</td>
|
||||
<td class="field-timestamp">
|
||||
<a class="timestamp-created">Created: 1.1.2013 12:22</a>
|
||||
<a class="timestamp-expires">Expires: 1.21.2013 12:22</a>
|
||||
</td>
|
||||
</tr>
|
||||
-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-menu">
|
||||
<div class="left">
|
||||
<button class="button-refresh">Reload</button>
|
||||
<div><input type="checkbox" class="filter-flag-force-own"> Show only own bans</div>
|
||||
<div><input type="checkbox" class="filter-flag-highlight-own"> Highlight own bans</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a class="entry-count-info"></a>
|
||||
<button class="button-close">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script class="jsrender-template" id="tmpl_client_ban" type="text/html">
|
||||
<div class="align_column">
|
||||
<div class="align_column" style="margin: 5px">
|
||||
<a>Name:</a>
|
||||
<input value="{{>client_name}}" readonly>
|
||||
</div>
|
||||
<div class="align_column" style="margin: 5px">
|
||||
<a>Reason:</a>
|
||||
<textarea style="height: 32px; resize: vertical; max-height: 150px; min-height: 32px" maxlength="512" class="ban_reason"></textarea>
|
||||
</div>
|
||||
<div class="align_row" style="margin: 5px; justify-content: space-between">
|
||||
<a>Duration:</a>
|
||||
<div class="align_row">
|
||||
<input type="number" value="1" class="ban_duration" style="margin-right: 7px" min="1">
|
||||
<select class="ban_duration_type">
|
||||
<option value="sec">seconds</option>
|
||||
<option value="min">minutes</option>
|
||||
<option value="hours">hours</option>
|
||||
<option value="days">days</option>
|
||||
<option value="perm">permanent</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group_box container-ban-type">
|
||||
<div class="header">Ban client by</div>
|
||||
<div class="content ban-types">
|
||||
<div>
|
||||
<input type="checkbox" class="ban-type-nickname">
|
||||
<a>Nickname</a>
|
||||
<div class="help-tip tip-right tip-small" checked>
|
||||
<p>
|
||||
Bans the client by his current nickname.<br>
|
||||
The currently nickname cant be used until the ban expired
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="ban-type-hardware-id" checked>
|
||||
<a>Hardware ID</a>
|
||||
<div class="help-tip tip-right tip-small">
|
||||
<p>
|
||||
Bans the client by his hardware id.<br>
|
||||
The hardware id has different meanings, depends on the users agent<br>
|
||||
TeaClient: The hardware id will be equal to the mac address<br>
|
||||
TeaWeb: The TeaSpeak web client hasn't a hardware id, it will be random<br>
|
||||
TeamSpeak 3 client: The hardware id will be a result of some hashes from hardware specific properties
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="ban-type-ip" checked>
|
||||
<a>IP Address</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script class="jsrender-template" id="tmpl_ban_entry" type="text/html">
|
||||
<tr class="ban-entry {{if server_id == 0}}ban-entry-global{{/if}} {{if flag_own}}ban-entry-own{{/if}}" ban-id={{>banid}} server-id={{>server_id}}>
|
||||
<td class="field-properties">
|
||||
{{if name }}<a class="property property-name">name={{>name}}</a>{{/if}}
|
||||
{{if ip }} <a class="property property-ip">ip={{>ip}}</a>{{/if}}
|
||||
{{if unique_id }} <a class="property property-unique-id">uid={{>unique_id}}</a>{{/if}}
|
||||
{{if hardware_id }}<a class="property property-hardware-id">hwid={{>hardware_id}}</a>{{/if}}
|
||||
</td>
|
||||
<td class="field-reason">{{>reason}}</td>
|
||||
<td class="field-invoker">{{>invoker_name}}</td>
|
||||
<td class="field-timestamp">
|
||||
<a class="timestamp-created">Created: {{fmt_date timestamp_created "DD.MM.YYYY HH:mm" /}}</a>
|
||||
<a class="timestamp-expire">Expire: {{if (!timestamp_expire || timestamp_expire.getTime() == 0) }}never{{else}}{{fmt_date timestamp_expire "DD.MM.YYYY HH:mm" /}}{{/if}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</script>
|
||||
<!-- `hwid` VARCHAR(" CLIENT_UID_LENGTH "), `uid` VARCHAR(" CLIENT_UID_LENGTH "), `name` VARCHAR(" CLIENT_NAME_LENGTH "), `ip` VARCHAR(128) -->
|
||||
<script class="jsrender-template" id="tmpl_ban_create" type="text/html">
|
||||
<div class="bancreate">
|
||||
<div class="frame-container">
|
||||
<div class="container container-ip">
|
||||
<a>IP:</a>
|
||||
<input class="input-ip" type="text" maxlength="128">
|
||||
</div>
|
||||
<div class="container container-name">
|
||||
<a>Name:</a>
|
||||
<input class="input-name" type="text" maxlength="128">
|
||||
<div class="container-name-type">
|
||||
<a>Interpret IP/Name as:</a>
|
||||
<select class="input-name-type">
|
||||
<option value="wildcard-4">Wildcard IPv4</option>
|
||||
<option value="wildcard-6">Wildcard IPv6</option>
|
||||
<option value="fixed" selected>Fixed string</option>
|
||||
<option value="regex">Regular Expression</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container container-uid">
|
||||
<a>Unique ID:</a>
|
||||
<div><input class="input-uid" type="text" maxlength="64"></div>
|
||||
</div>
|
||||
<div class="container container-hwid">
|
||||
<a>Hardware ID:</a>
|
||||
<input class="input-hwid" type="text" max="64">
|
||||
</div>
|
||||
<div class="container container-reason">
|
||||
<a>Reason</a>
|
||||
<textarea class="input-reason"></textarea>
|
||||
</div>
|
||||
<div class="container container-time">
|
||||
<a>Duration</a>
|
||||
<div class="container-time-input">
|
||||
<input type="number" value="1" class="input-time" style="margin-right: 7px" min="1">
|
||||
<select class="input-time-unit">
|
||||
<option value="sec">seconds</option>
|
||||
<option value="min">minutes</option>
|
||||
<option value="hours">hours</option>
|
||||
<option value="days">days</option>
|
||||
<option value="perm">permanent</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div>
|
||||
<div class="container-global">
|
||||
<input type="checkbox" class="input-global">
|
||||
<a>Use this ban as a global ban</a>
|
||||
<div class="help-tip tip-center tip-small">
|
||||
<p>
|
||||
Global bans are bans which apply instance wide.<br>
|
||||
This means that (if this rule apply to a victim) cant join <b>any</b> virtual server!<br>
|
||||
Global bans are by default shown to every server admin group,<br>
|
||||
but could only be created with query rights
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-buttons modal-button-group" style="text-align: right; margin-top: 3px; margin-bottom: 6px;">
|
||||
<button class="button-close">Cancel</button>
|
||||
<button class="button-success">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Music interface -->
|
||||
<script class="jsrender-template" id="tmpl_music_frame" type="text/html">
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue