A lot of changes

canary
WolverinDEV 2019-01-20 18:43:14 +01:00
parent 6e2ed6136e
commit 07b79ca0eb
28 changed files with 2435 additions and 403 deletions

View File

@ -1,8 +1,18 @@
# Changelog:
* **20.01.19**
- Added the possibility to change the remote volume of a bot
- Added a playlist management system
- Added BBCodes to the chat
- Added client links to BBCodes
- Added channel links to BBCodes
- Added the possibility to drag client into the chat
- Added function "create invite code" to the server
- Added parameters `connect_password` and `connect_password_hashed` to URL to pass passwords
* **19.01.19**
- Added multi user movement
- Improved connection profile error handling
- Added possibility to move the deviders
- Added possibility to move the dividers
* **23.12.18**
- Added query account management (since server 1.2.32b)

View File

@ -81,6 +81,7 @@
}
.container-select-info {
padding: 2px;
flex-grow: 1;
flex-shrink: 1;
display: flex;

View File

@ -44,7 +44,6 @@
font-size: 12px;
/*white-space: pre;*/
line-height: 1;
padding: 2px;
height: 100%;
display: flex;
flex-direction: column;
@ -378,3 +377,13 @@ html, body {
min-width: 500px;
overflow: hidden;
}
.icon-playlist-manage {
display: inline-block;
width: 32px;
height: 32px;
background: url('../img/music/playlist.svg') no-repeat;
background-position: -11px -9px;
background-size: 50px;
}

5
shared/css/htmltags.scss Normal file
View File

@ -0,0 +1,5 @@
.htmltag-client, .htmltag-channel {
color: blue;
font-weight: bold;
cursor: pointer;
}

View File

@ -0,0 +1,441 @@
.playlist-management {
height: 100%;
display: flex;
flex-direction: column;
.container {
display: flex;
flex-direction: column;
justify-content: stretch;
.header, .footer {
flex-grow: 0;
flex-shrink: 0;
}
.header {
display: flex;
flex-direction: row;
justify-content: stretch;
.buttons {
flex-grow: 0;
}
.search {
margin-left: 5px;
flex-grow: 1;
input {
width: 100%;
}
}
}
.playlist-list {
margin-top: 5px;
display: flex;
flex-grow: 1;
flex-direction: column;
justify-content: stretch;
$width_id: 80px;
$width_type: 150px;
$width_used: 40px;
.column {
&.column-id {
width: 80px;
text-align: center;
}
&.column-title {
width: calc(50% - 95px - 40px);
}
&.column-creator {
width: calc(50% - 95px - 40px);
text-align: center;
}
&.column-type {
width: 150px;
flex-grow: 0;
text-align: center;
}
&.column-used {
width: 40px;
flex-grow: 0;
text-align: center;
display: flex;
flex-direction: row;
justify-content: center;
align-self: center;
}
}
.playlist-list-header {
flex-grow: 0;
flex-shrink: 0;
display: flex;
flex-direction: row;
height: 20px;
.column {
border: 1px solid lightgray;
text-align: center;
}
}
.playlist-list-entries-container {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: start;
overflow-y: auto;
min-height: 250px;
.entry {
display: flex;
flex-direction: row;
.column {
margin-left: 2px;
}
cursor: pointer;
&.selected {
background-color: blue;
}
&.highlighted {
font-weight: bold;
}
}
&.scrollbar {
.column-title {
width: calc(50% - 95px - 40px + 30px)
}
.column-creator {
width: calc(50% - 95px - 40px + 30px)
}
}
}
}
.footer {
margin-top: 5px;
display: flex;
flex-direction: row;
justify-content: space-between;
.buttons {
display: flex;
flex-direction: row;
justify-content: stretch;
.highlight-own {
display: flex;
flex-direction: row;
justify-content: stretch;
margin-right: 10px;
align-self: center;
}
}
}
}
}
.playlist-edit {
.container {
display: flex;
flex-direction: column;
/* justify-content: stretch; */
.tab-content {
padding: 0; /* override tab-content setting */
}
.general-properties, .playback-properties {
padding: 5px;
width: 100%;
display: flex;
flex-direction: column;
.property {
display: flex;
flex-direction: row;
margin-bottom: 5px;
.key {
width: 150px;
flex-grow: 0;
}
.value {
flex-grow: 1;
flex-shrink: 1;
}
.checkbox-container {
input {
margin-left: 0;
}
}
&.property-description {
textarea {
resize: vertical;
max-height: 400px;
}
}
}
}
.playback-properties {
.property .key {
width: 175px;
}
}
.container-permissions {
padding: 5px;
display: flex;
flex-direction: row;
justify-content: space-around;
.permissions-list {
display: flex;
flex-direction: column;
.permission {
display: flex;
flex-direction: row;
margin-bottom: 5px;
.key {
width: 150px;
flex-grow: 0;
}
.value {
flex-grow: 1;
flex-shrink: 1;
}
}
}
}
.container-no-permissions {
background: lightgray;
padding: 50px;
text-align: center;
}
.container-songs {
padding: 5px;
.song-list {
min-height: 300px;
margin-top: 5px;
display: flex;
flex-grow: 1;
flex-direction: column;
justify-content: stretch;
.column {
&.column-id {
width: 50px;
}
&.column-url {
width: calc(100% - 140px)
}
&.column-loaded {
width: 50px;
flex-grow: 0;
display: flex;
justify-content: center;
flex-direction: row;
}
&.column-buttons {
width: 40px;
flex-grow: 0;
display: flex;
justify-content: center;
flex-direction: row;
.button {
display: flex;
flex-direction: column;
justify-content: center;
&:hover {
background: #00000033;
}
}
}
}
.song-list-header {
flex-grow: 0;
flex-shrink: 0;
display: flex;
flex-direction: row;
justify-content: center;
height: 20px;
.column {
border: 1px solid lightgray;
text-align: center;
}
}
.song-list-entries-container {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: start;
overflow-y: auto;
min-height: 250px;
.entry {
display: flex;
flex-direction: row;
.column {
margin-left: 2px;
}
cursor: pointer;
&.selected {
background-color: blue;
}
&.playing {
background-color: lightgreen;
}
}
&.scrollbar {
&.column-url {
width: calc(100% - 140px + 30px)
}
}
}
}
.footer {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
}
> .buttons {
margin-top: 5px;
align-self: flex-end;
button {
width: 100px;
}
}
}
}
.container-song-info {
display: flex;
flex-shrink: 1;
flex-direction: column;
.properties {
display: flex;
flex-direction: column;
padding-bottom: 5px;
.property {
display: flex;
flex-direction: row;
justify-content: stretch;
flex-shrink: 0;
.key {
width: 150px;
flex-grow: 0;
}
.value {
flex-grow: 1;
}
&.property-metadata-raw {
flex-direction: column;
flex-shrink: 1;
margin-top: 5px;
.line {
width: 100%;
display: flex;
flex-direction: row;
justify-content: stretch;
flex-shrink: 0;
}
textarea {
margin-top: 5px;
width: 100%;
max-height: 100%;
resize: vertical;
}
}
}
}
}
.container-song-add {
display: flex;
flex-shrink: 1;
flex-direction: column;
.properties {
display: flex;
flex-direction: column;
padding-bottom: 5px;
.property {
margin-bottom: 5px;
display: flex;
flex-direction: row;
justify-content: stretch;
flex-shrink: 0;
.key {
width: 150px;
flex-grow: 0;
}
.value {
flex-grow: 1;
}
}
}
}

View File

@ -52,6 +52,7 @@
<link rel="stylesheet" href="css/modal-bookmarks.css" type="text/css">
<link rel="stylesheet" href="css/modal-connect.css" type="text/css">
<link rel="stylesheet" href="css/modal-query.css" type="text/css">
<link rel="stylesheet" href="css/modal-playlist.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/modal-settings.css" type="text/css">
@ -61,6 +62,7 @@
<link rel="stylesheet" href="css/frame/SelectInfo.css" type="text/css">
<link rel="stylesheet" href="css/control_bar.css" type="text/css">
<link rel="stylesheet" href="css/context_menu.css" type="text/css">
<link rel="stylesheet" href="css/htmltags.css" type="text/css">
<link rel="stylesheet" href="vendor/bbcode/xbbcode.css" type="text/css">
<!-- https://localhost:9987/?forward_url=http%3A%2F%2Flocalhost%3A63344%2FWeb-Client%2Findex.php%3F_ijt%3D82b1uhmnh0a5l1n35nnjps5eid%26loader_ignore_age%3D1%26connect_default_host%3Dlocalhost%26default_connect_type%3Dforum%26default_connect_url%3Dtrue%26default_connect_type%3Dteamspeak%26default_connect_url%3Dlocalhost%253A9987 -->
<!-- PHP generated properies -->

View File

@ -71,6 +71,9 @@
</div>
<div style="width: 100%"></div>
<div class="button button-playlist-manage" title="{{tr 'Playlists' /}}">
<div class="icon-playlist-manage"></div>
</div>
<div class="button btn_banlist" title="{{tr 'Banlist' /}}">
<div class="icon_x32 client-ban_list"></div>
</div>
@ -116,7 +119,7 @@
<div class="chats"></div>
<div class="input">
<!--<div contentEditable="true" class="input_box"></div>-->
<textarea class="input_box" title=""></textarea>
<textarea class="input_box client-chat-box-field" placeholder="{{tr 'enter a chat message...' /}}"></textarea>
<button>Send</button>
</div>
</div>
@ -126,6 +129,7 @@
<div class="main_container container-info">
<div id="select_info" class="select_info" style="width: 100%; max-width: 100%">
<div class="container-banner"></div>
<!-- <div class="container-seperator horizontal" seperator-id="seperator-hostbanner-info"></div> -->
<div class="container-select-info"></div>
</div>
</div> <!-- Selection info -->
@ -893,7 +897,7 @@
<script class="jsrender-template" id="tmpl_change_volume" type="text/html">
<div style="display: flex; justify-content: center; vertical-align: center">
<input type="range" min="0" max="200" value="100" class="volume_slider" style="width: 100%">
<input type="range" min="0" max="{{>max_volume}}" value="100" class="volume_slider" style="width: 100%">
<div class="display_volume" style="width: 60px; align-self: center; text-align: center">&plusmn;0 %</div>
</div>
</script>
@ -1558,18 +1562,25 @@
<td>{{>property_client_description}}</td>
</tr>
{{/if}}
{{if property_client_uptime_mode == 0}}
<tr>
<td>{{tr "Livetime:"/}}</td>
<td class="update_onlinetime">{{:client_onlinetime}}</td>
</tr>
{{else}}
<tr>
<td>{{tr "Online since" /}}</td>
<td>{{tr "Server start" /}}</td>
</tr>
{{/if}}
<!-- player_volume -->
<tr>
<td>{{tr "Remote Volume:"/}}</td>
<td>{{*: data.property_player_volume * 100 }}%</td>
<td class="property-volume-remote">{{*: Math.floor(data.property_player_volume * 100) }}%</td>
</tr>
<tr>
<td>{{tr "Local Volume:"/}}</td>
<td>{{>sound_volume}}%</td>
<td class="property-volume-local">{{>sound_volume}}%</td>
</tr>
{{if song_url}}
<tr>
@ -1829,7 +1840,7 @@
</div>
<div class="footer">
<div class="info">
<a>loading...</a>
<a>{{tr "loading..." /}}</a>
</div>
<div class="buttons">
<button class="button-refresh">{{tr "Refresh" /}}</button>
@ -1848,6 +1859,289 @@
</div>
</script>
<script class="jsrender-template" id="tmpl_playlist_list" type="text/html">
<div class="playlist-management">
<div class="container">
<div class="header">
<div class="buttons">
<button class="button button-playlist-create">{{tr "Create playlist" /}}</button>
<button class="button button-playlist-delete">{{tr "Delete playlist" /}}</button>
<button class="button button-playlist-edit">{{tr "Edit playlist" /}}</button>
</div>
<div class="search">
<input class="input input-search" type="text" placeholder="search">
</div>
</div>
<div class="playlist-list">
<div class="playlist-list-header">
<div class="column column-id">{{tr "ID" /}}</div>
<div class="column column-title">{{tr "Title" /}}</div>
<div class="column column-creator">{{tr "Creator" /}}</div>
<div class="column column-type">{{tr "Type" /}}</div>
<div class="column column-used">{{tr "Used" /}}</div>
</div>
<div class="playlist-list-entries-container">
<div class="playlist-list-entries">
</div>
</div>
</div>
<div class="footer">
<div class="info">
<a>{{tr "loading..." /}}</a>
</div>
<div class="buttons">
<div class="highlight-own">
<div>Highlight own playlists</div>
<input class="button-highlight-own" type="checkbox">
</div>
<button class="button-refresh">{{tr "Refresh" /}}</button>
</div>
</div>
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_playlist_list-list_entry" type="text/html">
<div class="entry" playlist-owner-dbid="{{>playlist_owner_dbid}}">
<div class="column column-id">{{>playlist_id}}</div>
<div class="column column-title">{{>playlist_title}}</div>
<div class="column column-creator">{{>playlist_owner_name}}</div>
<div class="column column-type">
{{if playlist_type == 0}}
bot bound
{{else playlist_type == 1}}
global
{{else}}
{{>playlist_type}}
{{/if}}
</div>
<div class="column column-used">
{{if playlist_bot_id > 0}}
<div title="{{tr 'Playlist is in use by bot ' /}}{{>playlist_bot_id}}" class="icon client-apply"></div>
{{else}}
<div title="{{tr 'Playlist isnt use' /}}" class="icon client-delete"></div>
{{/if}}
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_playlist_edit" type="text/html">
<div class="playlist-edit">
<div class="container">
<div class="general-properties">
<div class="property property-owner">
<div class="key">{{tr "Playlist owner:" /}}</div>
<div class="value"></div>
</div>
<div class="property property-title">
<div class="key">{{tr "Title:" /}}</div>
<input type="text" class="value">
</div>
<div class="property property-description">
<div class="key">{{tr "Description:" /}}</div>
<textarea class="value"></textarea>
</div>
<div class="property property-type">
<div class="key">{{tr "Type:" /}}</div>
<select>
<option value="0">{{tr "Bot bound" /}}</option>
<option value="1">{{tr "Global" /}}</option>
</select>
</div>
</div>
<x-tab>
<x-entry>
<x-tag>{{tr "Songs" /}}</x-tag>
<x-content>
<div class="container-songs">
<div class="song-list">
<div class="song-list-header">
<div class="column column-id">{{tr "ID" /}}</div>
<div class="column column-url">{{tr "Url" /}}</div>
<div class="column column-loaded">{{tr "loaded" /}}</div>
<div class="column column-buttons"></div>
</div>
<div class="song-list-entries-container">
<div class="song-list-entries">
</div>
</div>
</div>
<div class="footer">
<div class="buttons">
<button class="button-song-add">{{tr "Add song" /}}</button>
<button class="button-refresh">{{tr "Refresh" /}}</button>
</div>
<div class="info-message"></div>
</div>
</div>
</x-content>
</x-entry>
<x-entry>
<x-tag>{{tr "Playback settings"/}}</x-tag>
<x-content>
<div class="playback-properties">
<div class="property property-replay-mode">
<div class="key">{{tr "Replay mode" /}}</div>
<select class="value">
<option value="0">{{tr "Normal"/}}</option>
<option value="1">{{tr "Loop list"/}}</option>
<option value="2">{{tr "Loop single entry"/}}</option>
<option value="3">{{tr "Shuffle"/}}</option>
</select>
</div>
<div class="property property-flag-delete-played">
<div class="key">{{tr "Delete played songs:" /}}</div>
<div class="checkbox-container">
<input type="checkbox" class="value">
</div>
</div>
<div class="property property-current-song">
<div class="key">{{tr "Current replaying song:" /}}</div>
<div class="value">0</div>
</div>
<div class="property property-flag-finished">
<div class="key">{{tr "Playlist finished:" /}}</div>
<div class="checkbox-container">
<input type="checkbox" class="value">
<!-- TODO add info text here -->
</div>
</div>
</div>
</x-content>
</x-entry>
<x-entry>
<x-tag>{{tr "Permissions" /}}</x-tag>
<x-content>
<div class="container-permissions">
<div class="group_box">
<div class="header">{{tr "Access/modify powers" /}}</div>
<div class="content permissions-list">
<div class="permission" permission="i_playlist_needed_view_power">
<div class="key">{{tr "View power" /}}</div>
<input type="number" min="0" value="0" class="value">
</div>
<div class="permission" permission="i_playlist_needed_modify_power">
<div class="key">{{tr "Modify" /}}</div>
<input type="number" min="0" value="0" class="value">
</div>
<div class="permission" permission="i_playlist_needed_permission_modify_power">
<div class="key">{{tr "Permission modify" /}}</div>
<input type="number" min="0" value="0" class="value">
</div>
<div class="permission" permission="i_playlist_needed_delete_power">
<div class="key">{{tr "Delete" /}}</div>
<input type="number" min="0" value="0" class="value">
</div>
</div>
</div>
<div class="group_box">
<div class="header">{{tr "Song management powers" /}}</div>
<div class="content permissions-list">
<div class="permission" permission="i_playlist_song_needed_add_power">
<div class="key">{{tr "Song add" /}}</div>
<input type="number" min="0" value="0" class="value">
</div>
<div class="permission" permission="i_playlist_song_needed_move_power">
<div class="key">{{tr "Song reorder" /}}</div>
<input type="number" min="0" value="0" class="value">
</div>
<div class="permission" permission="i_playlist_song_needed_remove_power">
<div class="key">{{tr "Song delete" /}}</div>
<input type="number" min="0" value="0" class="value">
</div>
</div>
</div>
</div>
<div class="container-no-permissions">
<div class="text">You dont have permissions to see playlist permissions!</div>
</div>
</x-content>
</x-entry>
</x-tab>
<div class="buttons">
<button class="button-save">{{tr "Save" /}}</button>
<button class="button-close">{{tr "Close" /}}</button>
</div>
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_playlist_edit-song_entry" type="text/html">
<div class="entry">
<div class="column column-id">{{>song_id}}</div>
<div class="column column-url">{{>song_url}}</div>
<div class="column column-loaded">{{>song_loaded}}</div>
<div class="column column-buttons">
<div class="button button-delete"><div class="icon client-delete"></div></div>
<div class="button button-info"><div class="icon client-about"></div></div>
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_playlist_edit-song_info" type="text/html">
<div class="container-song-info">
<div class="properties">
<div class="property property-song-id">
<div class="key">{{tr "Song ID:" /}}</div>
<div class="value">{{>song_id}}</div>
</div>
<div class="property property-song-order">
<div class="key">{{tr "Song order:" /}}</div>
<div class="value">{{>song_previous_song_id}}</div>
</div>
<div class="property property-song-url">
<div class="key">{{tr "URL:" /}}</div>
<div class="value">{{>song_url}}</div>
</div>
<div class="property property-song-url-loader">
<div class="key">{{tr "URL resolver:"/}}</div>
<div class="value">{{if song_url_loader}}{{>song_url_loader}}{{else}}{{tr "unset" /}}{{/if}}</div>
</div>
<div class="property property-song-loaded">
<div class="key">{{tr "Song loaded:" /}}</div>
<div class="value">{{if song_loaded}}{{tr "yes" /}}{{else}}{{tr "no" /}}{{/if}}</div>
</div>
{{if song_loaded}}
{{if metadata && false}}
{{tr "Display metdata here!" /}}
{{else}}
<div class="property property-metadata-raw">
<div class="line">
<div class="key">{{tr "Metadata:" /}}</div>
<div class="value"><button class="toggle-metadata">show</button></div>
</div>
<textarea>{{>song_metadata}}</textarea>
</div>
{{/if}}
{{/if}}
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_playlist_edit-song_add" type="text/html">
<div class="container-song-add">
<div class="properties">
<div class="property property-url">
<div class="key">{{tr "URL:" /}}</div>
<input class="value" />
</div>
<div class="property property-loader">
<div class="key">{{tr "URL loader:" /}}</div>
<input class="value" type="text" list="song-loader-list" />
<datalist id="song-loader-list">
<option>YouTube</option>
<option>FFMpeg</option>
<!-- <option>ChannelProvider</option> --> <!-- requires a special url, may add a UI with file select? -->
</datalist>
</div>
</div>
<div class="container-buttons">
<button class="button-add">{{tr "Add URL" /}}</button>
<button class="button-cancel">{{tr "Cancel" /}}</button>
</div>
</div>
</script>
<script class="jsrender-template" id="tmpl_manage_bookmarks" type="text/html">
<div class="modal-bookmarks">
<div class="group_box">

View File

@ -13,24 +13,26 @@ namespace MessageHelper {
return message.replace(/ /g, '&nbsp;').split(/<br>/);
}
export function formatElement(object: any) : JQuery[] {
export function formatElement(object: any, escape_html: boolean = true) : JQuery[] {
if($.isArray(object)) {
let result = [];
for(let element of object)
result.push(...this.formatElement(element));
result.push(...formatElement(element, escape_html));
return result;
} else if(typeof(object) == "string") {
if(object.length == 0) return [];
return this.htmlEscape(object).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 || idx + 1 == array.length ? "inline" : "") + "block").html(entry));
return escape_html ?
htmlEscape(object).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 || idx + 1 == array.length ? "inline" : "") + "block").html(entry == "" && idx != 0 ? "&nbsp;" : entry)) :
[$.spawn("div").css("display", "inline-block").html(object)];
} else if(typeof(object) === "object") {
if(object instanceof $)
return [object as any];
return this.formatElement("<unknwon object>");
} else if(typeof(object) === "function") return this.formatElement(object());
else if(typeof(object) === "undefined") return this.formatElement("<undefined>");
return formatElement("<unknwon object>");
} else if(typeof(object) === "function") return formatElement(object(), escape_html);
else if(typeof(object) === "undefined") return formatElement("<undefined>");
else if(typeof(object) === "number") return [$.spawn("a").text(object)];
return this.formatElement("<unknown object type " + typeof object + ">");
return formatElement("<unknown object type " + typeof object + ">");
}
export function formatMessage(pattern: string, ...objects: any[]) : JQuery[] {
@ -40,7 +42,7 @@ namespace MessageHelper {
do {
found = pattern.indexOf('{', found);
if(found == -1 || pattern.length <= found + 1) {
result.push(...this.formatElement(pattern.substr(begin)));
result.push(...formatElement(pattern.substr(begin)));
break;
}
@ -50,7 +52,7 @@ namespace MessageHelper {
continue;
}
result.push(...this.formatElement(pattern.substr(begin, found - begin))); //Append the text
result.push(...formatElement(pattern.substr(begin, found - begin))); //Append the text
let number;
let offset = 0;
@ -65,7 +67,7 @@ namespace MessageHelper {
if(objects.length < number)
console.warn(tr("Message to format contains invalid index (%o)"), number);
result.push(...this.formatElement(objects[number]));
result.push(...formatElement(objects[number]));
found = found + 1 + offset;
begin = found + 1;
console.log(tr("Offset: %d Number: %d"), offset, number);
@ -73,6 +75,30 @@ namespace MessageHelper {
return result;
}
export function bbcode_chat(message: string) : JQuery[] {
let result = XBBCODE.process({
text: message,
escapeHtml: true,
addInLineBreaks: false,
/* TODO make this configurable and allow IMG */
tag_whitelist: [
"b",
"i",
"u",
"color",
"url"
]
});
if(result.error) {
console.log("BBCode parse error: %o", result.errorQueue);
return formatElement(message);
}
return result.html.split("\n").map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 ? "inline" : "") + "block").html(entry == "" && idx != 0 ? "&nbsp;" : entry));
}
}
class ChatMessage {

View File

@ -151,7 +151,7 @@ class TSClient {
const parameters: string[] = [];
for(const key in properties)
parameters.push(encodeURIComponent(key) + "=" + encodeURIComponent(properties[key]));
parameters.push(key + "=" + encodeURIComponent(properties[key]));

View File

@ -3,6 +3,14 @@
/// <reference path="sound/Sounds.ts" />
/// <reference path="ui/modal/ModalPoke.ts" />
import noExitRuntime = Module.noExitRuntime;
enum ErrorID {
PERMISSION_ERROR = 2568,
EMPTY_RESULT = 0x0501,
PLAYLIST_IS_IN_USE = 0x2103
}
class CommandResult {
success: boolean;
id: number;
@ -406,6 +414,47 @@ interface QueryList {
queries: QueryListEntry[];
}
interface Playlist {
playlist_id: number;
playlist_bot_id: number;
playlist_title: string;
playlist_type: number;
playlist_owner_dbid: number;
playlist_owner_name: string;
needed_power_modify: number;
needed_power_permission_modify: number;
needed_power_delete: number;
needed_power_song_add: number;
needed_power_song_move: number;
needed_power_song_remove: number;
}
interface PlaylistInfo {
playlist_id: number,
playlist_title: string,
playlist_description: string,
playlist_type: number,
playlist_owner_dbid: number,
playlist_owner_name: string,
playlist_flag_delete_played: boolean,
playlist_flag_finished: boolean,
playlist_replay_mode: number,
playlist_current_song_id: number,
}
interface PlaylistSong {
song_id: number;
song_previous_song_id: number;
song_invoker: string;
song_url: string;
song_url_loader: string;
song_loaded: boolean;
song_metadata: string;
}
class CommandHelper {
readonly connection: ServerConnection;
@ -465,8 +514,9 @@ class CommandHelper {
this.connection.sendCommand("querylist", data).catch(error => {
if(error instanceof CommandResult) {
if(error.id == 0x0501) {
if(error.id == ErrorID.EMPTY_RESULT) {
resolve(undefined);
this.connection.commandHandler["notifyquerylist"] = undefined;
return;
}
}
@ -475,6 +525,137 @@ class CommandHelper {
});
}
request_playlist_list() : Promise<Playlist[]> {
return new Promise((resolve, reject) => {
const notify_handler = json => {
const result: Playlist[] = [];
for(const entry of json) {
try {
result.push({
playlist_id: parseInt(entry["playlist_id"]),
playlist_bot_id: parseInt(entry["playlist_bot_id"]),
playlist_title: entry["playlist_title"],
playlist_type: parseInt(entry["playlist_type"]),
playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]),
playlist_owner_name: entry["playlist_owner_name"],
needed_power_modify: parseInt(entry["needed_power_modify"]),
needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]),
needed_power_delete: parseInt(entry["needed_power_delete"]),
needed_power_song_add: parseInt(entry["needed_power_song_add"]),
needed_power_song_move: parseInt(entry["needed_power_song_move"]),
needed_power_song_remove: parseInt(entry["needed_power_song_remove"])
});
} catch(error) {
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error);
}
}
this.connection.commandHandler.unset_handler("notifyplaylistlist", notify_handler);
resolve(result);
};
this.connection.commandHandler.set_handler("notifyplaylistlist", notify_handler);
this.connection.sendCommand("playlistlist").catch(error => {
if(error instanceof CommandResult) {
if(error.id == ErrorID.EMPTY_RESULT) {
this.connection.commandHandler.unset_handler("notifyplaylistlist", notify_handler);
resolve([]);
return;
}
}
reject(error);
})
});
}
request_playlist_songs(playlist_id: number) : Promise<PlaylistSong[]> {
return new Promise((resolve, reject) => {
const notify_handler = json => {
if(json[0]["playlist_id"] != playlist_id) {
log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs"));
return;
}
const result: PlaylistSong[] = [];
for(const entry of json) {
try {
result.push({
song_id: parseInt(entry["song_id"]),
song_invoker: entry["song_invoker"],
song_previous_song_id: parseInt(entry["song_previous_song_id"]),
song_url: entry["song_url"],
song_url_loader: entry["song_url_loader"],
song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1",
song_metadata: entry["song_metadata"]
});
} catch(error) {
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error);
}
}
this.connection.commandHandler.unset_handler("notifyplaylistsonglist", notify_handler);
resolve(result);
};
this.connection.commandHandler.set_handler("notifyplaylistsonglist", notify_handler);
this.connection.sendCommand("playlistsonglist", {playlist_id: playlist_id}).catch(error => {
if(error instanceof CommandResult) {
if(error.id == ErrorID.EMPTY_RESULT) {
this.connection.commandHandler.unset_handler("notifyplaylistsonglist", notify_handler);
resolve([]);
return;
}
}
reject(error);
})
});
}
request_playlist_info(playlist_id: number) : Promise<PlaylistInfo> {
return new Promise((resolve, reject) => {
const notify_handler = json => {
if(json[0]["playlist_id"] != playlist_id) {
log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info"));
return;
}
json = json[0];
try {
//resolve
resolve({
playlist_id: parseInt(json["playlist_id"]),
playlist_title: json["playlist_title"],
playlist_description: json["playlist_description"],
playlist_type: parseInt(json["playlist_type"]),
playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]),
playlist_owner_name: json["playlist_owner_name"],
playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1",
playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1",
playlist_replay_mode: parseInt(json["playlist_replay_mode"]),
playlist_current_song_id: parseInt(json["playlist_current_song_id"]),
});
} catch(error) {
log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error);
reject("failed to parse info");
}
this.connection.commandHandler.unset_handler("notifyplaylistinfo", notify_handler);
};
this.connection.commandHandler.set_handler("notifyplaylistinfo", notify_handler);
this.connection.sendCommand("playlistinfo", {playlist_id: playlist_id}).catch(error => {
reject(error);
})
});
}
/**
* @deprecated
* Its just a workaround for the query management.
@ -550,6 +731,15 @@ class ConnectionCommandHandler {
this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged;
}
set_handler(command: string, handler: any) {
this[command] = handler;
}
unset_handler(command: string, handler?: any) {
if(handler && this[command] != handler) return;
this[command] = undefined;
}
handleCommandResult(json) {
json = json[0]; //Only one bulk
@ -721,9 +911,9 @@ class ConnectionCommandHandler {
else
sound.play(Sound.USER_ENTERED_CONNECT);
if(old_channel) {
chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}"), true, client.createChatTag(true), old_channel.createChatTag(true), channel.createChatTag(true));
chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}"), true, client.createChatTag(true), old_channel.generate_tag(true), channel.generate_tag(true));
} else {
chat.serverChat().appendMessage(tr("{0} connected to channel {1}"), true, client.createChatTag(true), channel.createChatTag(true));
chat.serverChat().appendMessage(tr("{0} connected to channel {1}"), true, client.createChatTag(true), channel.generate_tag(true));
}
} else if(json["reasonid"] == ViewReasonId.VREASON_MOVED) {
if(own_channel == channel)
@ -731,8 +921,8 @@ class ConnectionCommandHandler {
chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, moved by {3}"), true,
client.createChatTag(true),
old_channel ? old_channel.createChatTag(true) : undefined,
channel.createChatTag(true),
old_channel ? old_channel.generate_tag(true) : undefined,
channel.generate_tag(true),
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
);
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
@ -741,8 +931,8 @@ class ConnectionCommandHandler {
chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), true,
client.createChatTag(true),
old_channel ? old_channel.createChatTag(true) : undefined,
channel.createChatTag(true),
old_channel ? old_channel.generate_tag(true) : undefined,
channel.generate_tag(true),
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
json["reasonmsg"] > 0 ? " (" + json["msg"] + ")" : ""
);
@ -800,7 +990,7 @@ class ConnectionCommandHandler {
if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
chat.serverChat().appendMessage(tr("{0} disappeared from {1} to {2}"), true, client.createChatTag(true), channel_from.createChatTag(true), channel_to.createChatTag(true));
chat.serverChat().appendMessage(tr("{0} disappeared from {1} to {2}"), true, client.createChatTag(true), channel_from.generate_tag(true), channel_to.generate_tag(true));
if(channel_from == own_channel)
sound.play(Sound.USER_LEFT);
@ -886,8 +1076,8 @@ class ConnectionCommandHandler {
if(json["reasonid"] == ViewReasonId.VREASON_MOVED) {
chat.serverChat().appendMessage(self ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), true,
client.createChatTag(true),
channel_from ? channel_from.createChatTag(true) : undefined,
channel_to.createChatTag(true),
channel_from ? channel_from.generate_tag(true) : undefined,
channel_to.generate_tag(true),
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"])
);
if(self)
@ -899,8 +1089,8 @@ class ConnectionCommandHandler {
} else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
chat.serverChat().appendMessage(self ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), true,
client.createChatTag(true),
channel_from ? channel_from.createChatTag(true) : undefined,
channel_to.createChatTag(true)
channel_from ? channel_from.generate_tag(true) : undefined,
channel_to.generate_tag(true)
);
if(self) {} //If we do an action we wait for the error response
else if(own_channel == channel_to)
@ -910,8 +1100,8 @@ class ConnectionCommandHandler {
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
chat.serverChat().appendMessage(self ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), true,
client.createChatTag(true),
channel_from ? channel_from.createChatTag(true) : undefined,
channel_to.createChatTag(true),
channel_from ? channel_from.generate_tag(true) : undefined,
channel_to.generate_tag(true),
ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]),
json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : ""
);
@ -996,19 +1186,19 @@ class ConnectionCommandHandler {
}
if(invoker == this.connection._client.getClient()) {
sound.play(Sound.MESSAGE_SEND, { background_notification: true });
target.chat(true).appendMessage("{0}: {1}", true, this.connection._client.getClient().createChatTag(true), json["msg"]);
target.chat(true).appendMessage("{0}: {1}", true, this.connection._client.getClient().createChatTag(true), MessageHelper.bbcode_chat(json["msg"]));
} else {
sound.play(Sound.MESSAGE_RECEIVED, { background_notification: true });
invoker.chat(true).appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), json["msg"]);
invoker.chat(true).appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]));
}
} else if(mode == 2) {
if(json["invokerid"] == this.connection._client.clientId)
sound.play(Sound.MESSAGE_SEND, { background_notification: true });
else
sound.play(Sound.MESSAGE_RECEIVED, { background_notification: true });
chat.channelChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), json["msg"])
chat.channelChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]))
} else if(mode == 3) {
chat.serverChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), json["msg"]);
chat.serverChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"]));
}
}

View File

@ -1,4 +1,7 @@
$(document).bind("mousedown", function (e) {
let menu = $(".context-menu");
if(!menu.is(":visible")) return;
if ($(e.target).parents(".context-menu").length == 0) {
despawn_context_menu();
}

View File

@ -320,6 +320,8 @@ const loader_javascript = {
//Load UI
"js/ui/modal/ModalQuery.js",
"js/ui/modal/ModalQueryManage.js",
"js/ui/modal/ModalPlaylistList.js",
"js/ui/modal/ModalPlaylistEdit.js",
"js/ui/modal/ModalBookmarks.js",
"js/ui/modal/ModalConnect.js",
"js/ui/modal/ModalSettings.js",
@ -341,6 +343,7 @@ const loader_javascript = {
"js/ui/view.js",
"js/ui/client_move.js",
"js/ui/context_divider.js",
"js/ui/htmltags.js",
"js/ui/frames/SelectedItemInfo.js",
"js/ui/frames/ControlBar.js",

View File

@ -162,14 +162,20 @@ function main() {
//Modals.spawnSettingsModal();
//Modals.createChannelModal(undefined);
if(settings.static("connect_default") && settings.static("connect_address", "")) {
if(settings.static("connect_default", false) && settings.static("connect_address", "")) {
const profile_uuid = settings.static("connect_profile") as string;
const profile = profiles.find_profile(profile_uuid) || profiles.default_profile();
const address = settings.static("connect_address", "");
const username = settings.static("connect_username", "Another TeaSpeak user");
const password = settings.static("connect_password", "");
const password_hashed = settings.static("connect_password_hashed", false);
if(profile.valid()) {
globalClient.startConnection(address, profile, username);
globalClient.startConnection(address, profile, username, password.length > 0 ? {
password: password,
hashed: password_hashed
} : undefined);
} else {
Modals.spawnConnectModal({
url: address,

View File

@ -1,291 +1,345 @@
/// <reference path="../client.ts" />
enum PermissionType {
B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view",
B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view",
B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view",
B_SERVERINSTANCE_VIRTUALSERVER_LIST = "b_serverinstance_virtualserver_list",
B_SERVERINSTANCE_BINDING_LIST = "b_serverinstance_binding_list",
B_SERVERINSTANCE_PERMISSION_LIST = "b_serverinstance_permission_list",
B_SERVERINSTANCE_PERMISSION_FIND = "b_serverinstance_permission_find",
B_VIRTUALSERVER_CREATE = "b_virtualserver_create",
B_VIRTUALSERVER_DELETE = "b_virtualserver_delete",
B_VIRTUALSERVER_START_ANY = "b_virtualserver_start_any",
B_VIRTUALSERVER_STOP_ANY = "b_virtualserver_stop_any",
B_VIRTUALSERVER_CHANGE_MACHINE_ID = "b_virtualserver_change_machine_id",
B_VIRTUALSERVER_CHANGE_TEMPLATE = "b_virtualserver_change_template",
B_SERVERQUERY_LOGIN = "b_serverquery_login",
B_SERVERINSTANCE_TEXTMESSAGE_SEND = "b_serverinstance_textmessage_send",
B_SERVERINSTANCE_LOG_VIEW = "b_serverinstance_log_view",
B_SERVERINSTANCE_LOG_ADD = "b_serverinstance_log_add",
B_SERVERINSTANCE_STOP = "b_serverinstance_stop",
B_SERVERINSTANCE_MODIFY_SETTINGS = "b_serverinstance_modify_settings",
B_SERVERINSTANCE_MODIFY_QUERYGROUP = "b_serverinstance_modify_querygroup",
B_SERVERINSTANCE_MODIFY_TEMPLATES = "b_serverinstance_modify_templates",
B_VIRTUALSERVER_SELECT = "b_virtualserver_select",
B_VIRTUALSERVER_INFO_VIEW = "b_virtualserver_info_view",
B_VIRTUALSERVER_CONNECTIONINFO_VIEW = "b_virtualserver_connectioninfo_view",
B_VIRTUALSERVER_CHANNEL_LIST = "b_virtualserver_channel_list",
B_VIRTUALSERVER_CHANNEL_SEARCH = "b_virtualserver_channel_search",
B_VIRTUALSERVER_CLIENT_LIST = "b_virtualserver_client_list",
B_VIRTUALSERVER_CLIENT_SEARCH = "b_virtualserver_client_search",
B_VIRTUALSERVER_CLIENT_DBLIST = "b_virtualserver_client_dblist",
B_VIRTUALSERVER_CLIENT_DBSEARCH = "b_virtualserver_client_dbsearch",
B_VIRTUALSERVER_CLIENT_DBINFO = "b_virtualserver_client_dbinfo",
B_VIRTUALSERVER_PERMISSION_FIND = "b_virtualserver_permission_find",
B_VIRTUALSERVER_CUSTOM_SEARCH = "b_virtualserver_custom_search",
B_VIRTUALSERVER_START = "b_virtualserver_start",
B_VIRTUALSERVER_STOP = "b_virtualserver_stop",
B_VIRTUALSERVER_TOKEN_LIST = "b_virtualserver_token_list",
B_VIRTUALSERVER_TOKEN_ADD = "b_virtualserver_token_add",
B_VIRTUALSERVER_TOKEN_USE = "b_virtualserver_token_use",
B_VIRTUALSERVER_TOKEN_DELETE = "b_virtualserver_token_delete",
B_VIRTUALSERVER_LOG_VIEW = "b_virtualserver_log_view",
B_VIRTUALSERVER_LOG_ADD = "b_virtualserver_log_add",
B_VIRTUALSERVER_JOIN_IGNORE_PASSWORD = "b_virtualserver_join_ignore_password",
B_VIRTUALSERVER_NOTIFY_REGISTER = "b_virtualserver_notify_register",
B_VIRTUALSERVER_NOTIFY_UNREGISTER = "b_virtualserver_notify_unregister",
B_VIRTUALSERVER_SNAPSHOT_CREATE = "b_virtualserver_snapshot_create",
B_VIRTUALSERVER_SNAPSHOT_DEPLOY = "b_virtualserver_snapshot_deploy",
B_VIRTUALSERVER_PERMISSION_RESET = "b_virtualserver_permission_reset",
B_VIRTUALSERVER_MODIFY_NAME = "b_virtualserver_modify_name",
B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE = "b_virtualserver_modify_welcomemessage",
B_VIRTUALSERVER_MODIFY_MAXCLIENTS = "b_virtualserver_modify_maxclients",
B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS = "b_virtualserver_modify_reserved_slots",
B_VIRTUALSERVER_MODIFY_PASSWORD = "b_virtualserver_modify_password",
B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP = "b_virtualserver_modify_default_servergroup",
B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP = "b_virtualserver_modify_default_channelgroup",
B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP = "b_virtualserver_modify_default_channeladmingroup",
B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE = "b_virtualserver_modify_channel_forced_silence",
B_VIRTUALSERVER_MODIFY_COMPLAIN = "b_virtualserver_modify_complain",
B_VIRTUALSERVER_MODIFY_ANTIFLOOD = "b_virtualserver_modify_antiflood",
B_VIRTUALSERVER_MODIFY_FT_SETTINGS = "b_virtualserver_modify_ft_settings",
B_VIRTUALSERVER_MODIFY_FT_QUOTAS = "b_virtualserver_modify_ft_quotas",
B_VIRTUALSERVER_MODIFY_HOSTMESSAGE = "b_virtualserver_modify_hostmessage",
B_VIRTUALSERVER_MODIFY_HOSTBANNER = "b_virtualserver_modify_hostbanner",
B_VIRTUALSERVER_MODIFY_HOSTBUTTON = "b_virtualserver_modify_hostbutton",
B_VIRTUALSERVER_MODIFY_PORT = "b_virtualserver_modify_port",
B_VIRTUALSERVER_MODIFY_HOST = "b_virtualserver_modify_host",
B_VIRTUALSERVER_MODIFY_AUTOSTART = "b_virtualserver_modify_autostart",
B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL = "b_virtualserver_modify_needed_identity_security_level",
B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR = "b_virtualserver_modify_priority_speaker_dimm_modificator",
B_VIRTUALSERVER_MODIFY_LOG_SETTINGS = "b_virtualserver_modify_log_settings",
B_VIRTUALSERVER_MODIFY_MIN_CLIENT_VERSION = "b_virtualserver_modify_min_client_version",
B_VIRTUALSERVER_MODIFY_ICON_ID = "b_virtualserver_modify_icon_id",
B_VIRTUALSERVER_MODIFY_WEBLIST = "b_virtualserver_modify_weblist",
B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE = "b_virtualserver_modify_codec_encryption_mode",
B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS = "b_virtualserver_modify_temporary_passwords",
B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS_OWN = "b_virtualserver_modify_temporary_passwords_own",
B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT = "b_virtualserver_modify_channel_temp_delete_delay_default",
B_VIRTUALSERVER_MODIFY_MUSIC_BOT_LIMIT = "b_virtualserver_modify_music_bot_limit",
B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES = "b_virtualserver_modify_music_bot_limit",
I_CHANNEL_MIN_DEPTH = "i_channel_min_depth",
I_CHANNEL_MAX_DEPTH = "i_channel_max_depth",
B_CHANNEL_GROUP_INHERITANCE_END = "b_channel_group_inheritance_end",
I_CHANNEL_PERMISSION_MODIFY_POWER = "i_channel_permission_modify_power",
I_CHANNEL_NEEDED_PERMISSION_MODIFY_POWER = "i_channel_needed_permission_modify_power",
B_CHANNEL_INFO_VIEW = "b_channel_info_view",
B_CHANNEL_CREATE_CHILD = "b_channel_create_child",
B_CHANNEL_CREATE_PERMANENT = "b_channel_create_permanent",
B_CHANNEL_CREATE_SEMI_PERMANENT = "b_channel_create_semi_permanent",
B_CHANNEL_CREATE_TEMPORARY = "b_channel_create_temporary",
B_CHANNEL_CREATE_PRIVATE = "b_channel_create_private",
B_CHANNEL_CREATE_WITH_TOPIC = "b_channel_create_with_topic",
B_CHANNEL_CREATE_WITH_DESCRIPTION = "b_channel_create_with_description",
B_CHANNEL_CREATE_WITH_PASSWORD = "b_channel_create_with_password",
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8 = "b_channel_create_modify_with_codec_speex8",
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16 = "b_channel_create_modify_with_codec_speex16",
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32 = "b_channel_create_modify_with_codec_speex32",
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48 = "b_channel_create_modify_with_codec_celtmono48",
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE = "b_channel_create_modify_with_codec_opusvoice",
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC = "b_channel_create_modify_with_codec_opusmusic",
I_CHANNEL_CREATE_MODIFY_WITH_CODEC_MAXQUALITY = "i_channel_create_modify_with_codec_maxquality",
I_CHANNEL_CREATE_MODIFY_WITH_CODEC_LATENCY_FACTOR_MIN = "i_channel_create_modify_with_codec_latency_factor_min",
B_CHANNEL_CREATE_WITH_MAXCLIENTS = "b_channel_create_with_maxclients",
B_CHANNEL_CREATE_WITH_MAXFAMILYCLIENTS = "b_channel_create_with_maxfamilyclients",
B_CHANNEL_CREATE_WITH_SORTORDER = "b_channel_create_with_sortorder",
B_CHANNEL_CREATE_WITH_DEFAULT = "b_channel_create_with_default",
B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER = "b_channel_create_with_needed_talk_power",
B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD = "b_channel_create_modify_with_force_password",
I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY = "i_channel_create_modify_with_temp_delete_delay",
B_CHANNEL_MODIFY_PARENT = "b_channel_modify_parent",
B_CHANNEL_MODIFY_MAKE_DEFAULT = "b_channel_modify_make_default",
B_CHANNEL_MODIFY_MAKE_PERMANENT = "b_channel_modify_make_permanent",
B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT = "b_channel_modify_make_semi_permanent",
B_CHANNEL_MODIFY_MAKE_TEMPORARY = "b_channel_modify_make_temporary",
B_CHANNEL_MODIFY_NAME = "b_channel_modify_name",
B_CHANNEL_MODIFY_TOPIC = "b_channel_modify_topic",
B_CHANNEL_MODIFY_DESCRIPTION = "b_channel_modify_description",
B_CHANNEL_MODIFY_PASSWORD = "b_channel_modify_password",
B_CHANNEL_MODIFY_CODEC = "b_channel_modify_codec",
B_CHANNEL_MODIFY_CODEC_QUALITY = "b_channel_modify_codec_quality",
B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR = "b_channel_modify_codec_latency_factor",
B_CHANNEL_MODIFY_MAXCLIENTS = "b_channel_modify_maxclients",
B_CHANNEL_MODIFY_MAXFAMILYCLIENTS = "b_channel_modify_maxfamilyclients",
B_CHANNEL_MODIFY_SORTORDER = "b_channel_modify_sortorder",
B_CHANNEL_MODIFY_NEEDED_TALK_POWER = "b_channel_modify_needed_talk_power",
I_CHANNEL_MODIFY_POWER = "i_channel_modify_power",
I_CHANNEL_NEEDED_MODIFY_POWER = "i_channel_needed_modify_power",
B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED = "b_channel_modify_make_codec_encrypted",
B_CHANNEL_MODIFY_TEMP_DELETE_DELAY = "b_channel_modify_temp_delete_delay",
B_CHANNEL_DELETE_PERMANENT = "b_channel_delete_permanent",
B_CHANNEL_DELETE_SEMI_PERMANENT = "b_channel_delete_semi_permanent",
B_CHANNEL_DELETE_TEMPORARY = "b_channel_delete_temporary",
B_CHANNEL_DELETE_FLAG_FORCE = "b_channel_delete_flag_force",
I_CHANNEL_DELETE_POWER = "i_channel_delete_power",
I_CHANNEL_NEEDED_DELETE_POWER = "i_channel_needed_delete_power",
B_CHANNEL_JOIN_PERMANENT = "b_channel_join_permanent",
B_CHANNEL_JOIN_SEMI_PERMANENT = "b_channel_join_semi_permanent",
B_CHANNEL_JOIN_TEMPORARY = "b_channel_join_temporary",
B_CHANNEL_JOIN_IGNORE_PASSWORD = "b_channel_join_ignore_password",
B_CHANNEL_JOIN_IGNORE_MAXCLIENTS = "b_channel_join_ignore_maxclients",
I_CHANNEL_JOIN_POWER = "i_channel_join_power",
I_CHANNEL_NEEDED_JOIN_POWER = "i_channel_needed_join_power",
I_CHANNEL_SUBSCRIBE_POWER = "i_channel_subscribe_power",
I_CHANNEL_NEEDED_SUBSCRIBE_POWER = "i_channel_needed_subscribe_power",
I_CHANNEL_DESCRIPTION_VIEW_POWER = "i_channel_description_view_power",
I_CHANNEL_NEEDED_DESCRIPTION_VIEW_POWER = "i_channel_needed_description_view_power",
I_ICON_ID = "i_icon_id",
I_MAX_ICON_FILESIZE = "i_max_icon_filesize",
B_ICON_MANAGE = "b_icon_manage",
B_GROUP_IS_PERMANENT = "b_group_is_permanent",
I_GROUP_AUTO_UPDATE_TYPE = "i_group_auto_update_type",
I_GROUP_AUTO_UPDATE_MAX_VALUE = "i_group_auto_update_max_value",
I_GROUP_SORT_ID = "i_group_sort_id",
I_GROUP_SHOW_NAME_IN_TREE = "i_group_show_name_in_tree",
B_VIRTUALSERVER_SERVERGROUP_LIST = "b_virtualserver_servergroup_list",
B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST = "b_virtualserver_servergroup_permission_list",
B_VIRTUALSERVER_SERVERGROUP_CLIENT_LIST = "b_virtualserver_servergroup_client_list",
B_VIRTUALSERVER_CHANNELGROUP_LIST = "b_virtualserver_channelgroup_list",
B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST = "b_virtualserver_channelgroup_permission_list",
B_VIRTUALSERVER_CHANNELGROUP_CLIENT_LIST = "b_virtualserver_channelgroup_client_list",
B_VIRTUALSERVER_CLIENT_PERMISSION_LIST = "b_virtualserver_client_permission_list",
B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST = "b_virtualserver_channel_permission_list",
B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST = "b_virtualserver_channelclient_permission_list",
B_VIRTUALSERVER_SERVERGROUP_CREATE = "b_virtualserver_servergroup_create",
B_VIRTUALSERVER_CHANNELGROUP_CREATE = "b_virtualserver_channelgroup_create",
I_SERVER_GROUP_MODIFY_POWER = "i_server_group_modify_power",
I_SERVER_GROUP_NEEDED_MODIFY_POWER = "i_server_group_needed_modify_power",
I_SERVER_GROUP_MEMBER_ADD_POWER = "i_server_group_member_add_power",
I_SERVER_GROUP_NEEDED_MEMBER_ADD_POWER = "i_server_group_needed_member_add_power",
I_SERVER_GROUP_MEMBER_REMOVE_POWER = "i_server_group_member_remove_power",
I_SERVER_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_server_group_needed_member_remove_power",
I_CHANNEL_GROUP_MODIFY_POWER = "i_channel_group_modify_power",
I_CHANNEL_GROUP_NEEDED_MODIFY_POWER = "i_channel_group_needed_modify_power",
I_CHANNEL_GROUP_MEMBER_ADD_POWER = "i_channel_group_member_add_power",
I_CHANNEL_GROUP_NEEDED_MEMBER_ADD_POWER = "i_channel_group_needed_member_add_power",
I_CHANNEL_GROUP_MEMBER_REMOVE_POWER = "i_channel_group_member_remove_power",
I_CHANNEL_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_channel_group_needed_member_remove_power",
I_GROUP_MEMBER_ADD_POWER = "i_group_member_add_power",
I_GROUP_NEEDED_MEMBER_ADD_POWER = "i_group_needed_member_add_power",
I_GROUP_MEMBER_REMOVE_POWER = "i_group_member_remove_power",
I_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_group_needed_member_remove_power",
I_GROUP_MODIFY_POWER = "i_group_modify_power",
I_GROUP_NEEDED_MODIFY_POWER = "i_group_needed_modify_power",
I_DISPLAYED_GROUP_MEMBER_ADD_POWER = "i_displayed_group_member_add_power",
I_DISPLAYED_GROUP_NEEDED_MEMBER_ADD_POWER = "i_displayed_group_needed_member_add_power",
I_DISPLAYED_GROUP_MEMBER_REMOVE_POWER = "i_displayed_group_member_remove_power",
I_DISPLAYED_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_displayed_group_needed_member_remove_power",
I_DISPLAYED_GROUP_MODIFY_POWER = "i_displayed_group_modify_power",
I_DISPLAYED_GROUP_NEEDED_MODIFY_POWER = "i_displayed_group_needed_modify_power",
I_PERMISSION_MODIFY_POWER = "i_permission_modify_power",
B_PERMISSION_MODIFY_POWER_IGNORE = "b_permission_modify_power_ignore",
B_VIRTUALSERVER_SERVERGROUP_DELETE = "b_virtualserver_servergroup_delete",
B_VIRTUALSERVER_CHANNELGROUP_DELETE = "b_virtualserver_channelgroup_delete",
I_CLIENT_PERMISSION_MODIFY_POWER = "i_client_permission_modify_power",
I_CLIENT_NEEDED_PERMISSION_MODIFY_POWER = "i_client_needed_permission_modify_power",
I_CLIENT_MAX_CLONES_UID = "i_client_max_clones_uid",
I_CLIENT_MAX_IDLETIME = "i_client_max_idletime",
I_CLIENT_MAX_AVATAR_FILESIZE = "i_client_max_avatar_filesize",
I_CLIENT_MAX_CHANNEL_SUBSCRIPTIONS = "i_client_max_channel_subscriptions",
B_CLIENT_IS_PRIORITY_SPEAKER = "b_client_is_priority_speaker",
B_CLIENT_SKIP_CHANNELGROUP_PERMISSIONS = "b_client_skip_channelgroup_permissions",
B_CLIENT_FORCE_PUSH_TO_TALK = "b_client_force_push_to_talk",
B_CLIENT_IGNORE_BANS = "b_client_ignore_bans",
B_CLIENT_IGNORE_ANTIFLOOD = "b_client_ignore_antiflood",
B_CLIENT_ISSUE_CLIENT_QUERY_COMMAND = "b_client_issue_client_query_command",
B_CLIENT_USE_RESERVED_SLOT = "b_client_use_reserved_slot",
B_CLIENT_USE_CHANNEL_COMMANDER = "b_client_use_channel_commander",
B_CLIENT_REQUEST_TALKER = "b_client_request_talker",
B_CLIENT_AVATAR_DELETE_OTHER = "b_client_avatar_delete_other",
B_CLIENT_IS_STICKY = "b_client_is_sticky",
B_CLIENT_IGNORE_STICKY = "b_client_ignore_sticky",
B_CLIENT_MUSIC_CHANNEL_LIST = "b_client_music_channel_list",
B_CLIENT_MUSIC_SERVER_LIST = "b_client_music_server_list",
I_CLIENT_MUSIC_INFO = "i_client_music_info",
I_CLIENT_MUSIC_NEEDED_INFO = "i_client_music_needed_info",
B_CLIENT_INFO_VIEW = "b_client_info_view",
B_CLIENT_PERMISSIONOVERVIEW_VIEW = "b_client_permissionoverview_view",
B_CLIENT_PERMISSIONOVERVIEW_OWN = "b_client_permissionoverview_own",
B_CLIENT_REMOTEADDRESS_VIEW = "b_client_remoteaddress_view",
I_CLIENT_SERVERQUERY_VIEW_POWER = "i_client_serverquery_view_power",
I_CLIENT_NEEDED_SERVERQUERY_VIEW_POWER = "i_client_needed_serverquery_view_power",
B_CLIENT_CUSTOM_INFO_VIEW = "b_client_custom_info_view",
I_CLIENT_KICK_FROM_SERVER_POWER = "i_client_kick_from_server_power",
I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER = "i_client_needed_kick_from_server_power",
I_CLIENT_KICK_FROM_CHANNEL_POWER = "i_client_kick_from_channel_power",
I_CLIENT_NEEDED_KICK_FROM_CHANNEL_POWER = "i_client_needed_kick_from_channel_power",
I_CLIENT_BAN_POWER = "i_client_ban_power",
I_CLIENT_NEEDED_BAN_POWER = "i_client_needed_ban_power",
I_CLIENT_MOVE_POWER = "i_client_move_power",
I_CLIENT_NEEDED_MOVE_POWER = "i_client_needed_move_power",
I_CLIENT_COMPLAIN_POWER = "i_client_complain_power",
I_CLIENT_NEEDED_COMPLAIN_POWER = "i_client_needed_complain_power",
B_CLIENT_COMPLAIN_LIST = "b_client_complain_list",
B_CLIENT_COMPLAIN_DELETE_OWN = "b_client_complain_delete_own",
B_CLIENT_COMPLAIN_DELETE = "b_client_complain_delete",
B_CLIENT_BAN_LIST = "b_client_ban_list",
B_CLIENT_BAN_LIST_GLOBAL = "b_client_ban_list_global",
B_CLIENT_BAN_CREATE = "b_client_ban_create",
B_CLIENT_BAN_CREATE_GLOBAL = "b_client_ban_create_global",
B_CLIENT_BAN_EDIT = "b_client_ban_edit",
B_CLIENT_BAN_EDIT_GLOBAL = "b_client_ban_edit_global",
B_CLIENT_BAN_DELETE_OWN = "b_client_ban_delete_own",
B_CLIENT_BAN_DELETE = "b_client_ban_delete",
B_CLIENT_BAN_DELETE_OWN_GLOBAL = "b_client_ban_delete_own_global",
B_CLIENT_BAN_DELETE_GLOBAL = "b_client_ban_delete_global",
I_CLIENT_BAN_MAX_BANTIME = "i_client_ban_max_bantime",
I_CLIENT_PRIVATE_TEXTMESSAGE_POWER = "i_client_private_textmessage_power",
I_CLIENT_NEEDED_PRIVATE_TEXTMESSAGE_POWER = "i_client_needed_private_textmessage_power",
B_CLIENT_SERVER_TEXTMESSAGE_SEND = "b_client_server_textmessage_send",
B_CLIENT_CHANNEL_TEXTMESSAGE_SEND = "b_client_channel_textmessage_send",
B_CLIENT_OFFLINE_TEXTMESSAGE_SEND = "b_client_offline_textmessage_send",
I_CLIENT_TALK_POWER = "i_client_talk_power",
I_CLIENT_NEEDED_TALK_POWER = "i_client_needed_talk_power",
I_CLIENT_POKE_POWER = "i_client_poke_power",
I_CLIENT_NEEDED_POKE_POWER = "i_client_needed_poke_power",
B_CLIENT_SET_FLAG_TALKER = "b_client_set_flag_talker",
I_CLIENT_WHISPER_POWER = "i_client_whisper_power",
I_CLIENT_NEEDED_WHISPER_POWER = "i_client_needed_whisper_power",
B_CLIENT_MODIFY_DESCRIPTION = "b_client_modify_description",
B_CLIENT_MODIFY_OWN_DESCRIPTION = "b_client_modify_own_description",
B_CLIENT_MODIFY_DBPROPERTIES = "b_client_modify_dbproperties",
B_CLIENT_DELETE_DBPROPERTIES = "b_client_delete_dbproperties",
B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN = "b_client_create_modify_serverquery_login",
B_CLIENT_MUSIC_CREATE = "b_client_music_create",
I_CLIENT_MUSIC_LIMIT = "i_client_music_limit",
I_CLIENT_MUSIC_DELETE_POWER = "i_client_music_delete_power",
I_CLIENT_MUSIC_NEEDED_DELETE_POWER = "i_client_music_needed_delete_power",
I_CLIENT_MUSIC_PLAY_POWER = "i_client_music_play_power",
I_CLIENT_MUSIC_NEEDED_PLAY_POWER = "i_client_music_needed_play_power",
I_CLIENT_MUSIC_RENAME_POWER = "i_client_music_rename_power",
I_CLIENT_MUSIC_NEEDED_RENAME_POWER = "i_client_music_needed_rename_power",
B_FT_IGNORE_PASSWORD = "b_ft_ignore_password",
B_FT_TRANSFER_LIST = "b_ft_transfer_list",
I_FT_FILE_UPLOAD_POWER = "i_ft_file_upload_power",
I_FT_NEEDED_FILE_UPLOAD_POWER = "i_ft_needed_file_upload_power",
I_FT_FILE_DOWNLOAD_POWER = "i_ft_file_download_power",
I_FT_NEEDED_FILE_DOWNLOAD_POWER = "i_ft_needed_file_download_power",
I_FT_FILE_DELETE_POWER = "i_ft_file_delete_power",
I_FT_NEEDED_FILE_DELETE_POWER = "i_ft_needed_file_delete_power",
I_FT_FILE_RENAME_POWER = "i_ft_file_rename_power",
I_FT_NEEDED_FILE_RENAME_POWER = "i_ft_needed_file_rename_power",
I_FT_FILE_BROWSE_POWER = "i_ft_file_browse_power",
I_FT_NEEDED_FILE_BROWSE_POWER = "i_ft_needed_file_browse_power",
I_FT_DIRECTORY_CREATE_POWER = "i_ft_directory_create_power",
I_FT_NEEDED_DIRECTORY_CREATE_POWER = "i_ft_needed_directory_create_power",
I_FT_QUOTA_MB_DOWNLOAD_PER_CLIENT = "i_ft_quota_mb_download_per_client",
I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client"
B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view", /* Permission ID: 1 */
B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view", /* Permission ID: 2 */
B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view", /* Permission ID: 3 */
B_SERVERINSTANCE_VIRTUALSERVER_LIST = "b_serverinstance_virtualserver_list", /* Permission ID: 4 */
B_SERVERINSTANCE_BINDING_LIST = "b_serverinstance_binding_list", /* Permission ID: 5 */
B_SERVERINSTANCE_PERMISSION_LIST = "b_serverinstance_permission_list", /* Permission ID: 6 */
B_SERVERINSTANCE_PERMISSION_FIND = "b_serverinstance_permission_find", /* Permission ID: 7 */
B_VIRTUALSERVER_CREATE = "b_virtualserver_create", /* Permission ID: 8 */
B_VIRTUALSERVER_DELETE = "b_virtualserver_delete", /* Permission ID: 9 */
B_VIRTUALSERVER_START_ANY = "b_virtualserver_start_any", /* Permission ID: 10 */
B_VIRTUALSERVER_STOP_ANY = "b_virtualserver_stop_any", /* Permission ID: 11 */
B_VIRTUALSERVER_CHANGE_MACHINE_ID = "b_virtualserver_change_machine_id", /* Permission ID: 12 */
B_VIRTUALSERVER_CHANGE_TEMPLATE = "b_virtualserver_change_template", /* Permission ID: 13 */
B_SERVERQUERY_LOGIN = "b_serverquery_login", /* Permission ID: 14 */
B_SERVERINSTANCE_TEXTMESSAGE_SEND = "b_serverinstance_textmessage_send", /* Permission ID: 15 */
B_SERVERINSTANCE_LOG_VIEW = "b_serverinstance_log_view", /* Permission ID: 16 */
B_SERVERINSTANCE_LOG_ADD = "b_serverinstance_log_add", /* Permission ID: 17 */
B_SERVERINSTANCE_STOP = "b_serverinstance_stop", /* Permission ID: 18 */
B_SERVERINSTANCE_MODIFY_SETTINGS = "b_serverinstance_modify_settings", /* Permission ID: 19 */
B_SERVERINSTANCE_MODIFY_QUERYGROUP = "b_serverinstance_modify_querygroup", /* Permission ID: 20 */
B_SERVERINSTANCE_MODIFY_TEMPLATES = "b_serverinstance_modify_templates", /* Permission ID: 21 */
B_VIRTUALSERVER_SELECT = "b_virtualserver_select", /* Permission ID: 22 */
B_VIRTUALSERVER_SELECT_GODMODE = "b_virtualserver_select_godmode", /* Permission ID: 23 */
B_VIRTUALSERVER_INFO_VIEW = "b_virtualserver_info_view", /* Permission ID: 24 */
B_VIRTUALSERVER_CONNECTIONINFO_VIEW = "b_virtualserver_connectioninfo_view", /* Permission ID: 25 */
B_VIRTUALSERVER_CHANNEL_LIST = "b_virtualserver_channel_list", /* Permission ID: 26 */
B_VIRTUALSERVER_CHANNEL_SEARCH = "b_virtualserver_channel_search", /* Permission ID: 27 */
B_VIRTUALSERVER_CLIENT_LIST = "b_virtualserver_client_list", /* Permission ID: 28 */
B_VIRTUALSERVER_CLIENT_SEARCH = "b_virtualserver_client_search", /* Permission ID: 29 */
B_VIRTUALSERVER_CLIENT_DBLIST = "b_virtualserver_client_dblist", /* Permission ID: 30 */
B_VIRTUALSERVER_CLIENT_DBSEARCH = "b_virtualserver_client_dbsearch", /* Permission ID: 31 */
B_VIRTUALSERVER_CLIENT_DBINFO = "b_virtualserver_client_dbinfo", /* Permission ID: 32 */
B_VIRTUALSERVER_PERMISSION_FIND = "b_virtualserver_permission_find", /* Permission ID: 33 */
B_VIRTUALSERVER_CUSTOM_SEARCH = "b_virtualserver_custom_search", /* Permission ID: 34 */
B_VIRTUALSERVER_START = "b_virtualserver_start", /* Permission ID: 35 */
B_VIRTUALSERVER_STOP = "b_virtualserver_stop", /* Permission ID: 36 */
B_VIRTUALSERVER_TOKEN_LIST = "b_virtualserver_token_list", /* Permission ID: 37 */
B_VIRTUALSERVER_TOKEN_ADD = "b_virtualserver_token_add", /* Permission ID: 38 */
B_VIRTUALSERVER_TOKEN_USE = "b_virtualserver_token_use", /* Permission ID: 39 */
B_VIRTUALSERVER_TOKEN_DELETE = "b_virtualserver_token_delete", /* Permission ID: 40 */
B_VIRTUALSERVER_LOG_VIEW = "b_virtualserver_log_view", /* Permission ID: 41 */
B_VIRTUALSERVER_LOG_ADD = "b_virtualserver_log_add", /* Permission ID: 42 */
B_VIRTUALSERVER_JOIN_IGNORE_PASSWORD = "b_virtualserver_join_ignore_password", /* Permission ID: 43 */
B_VIRTUALSERVER_NOTIFY_REGISTER = "b_virtualserver_notify_register", /* Permission ID: 44 */
B_VIRTUALSERVER_NOTIFY_UNREGISTER = "b_virtualserver_notify_unregister", /* Permission ID: 45 */
B_VIRTUALSERVER_SNAPSHOT_CREATE = "b_virtualserver_snapshot_create", /* Permission ID: 46 */
B_VIRTUALSERVER_SNAPSHOT_DEPLOY = "b_virtualserver_snapshot_deploy", /* Permission ID: 47 */
B_VIRTUALSERVER_PERMISSION_RESET = "b_virtualserver_permission_reset", /* Permission ID: 48 */
B_VIRTUALSERVER_MODIFY_NAME = "b_virtualserver_modify_name", /* Permission ID: 49 */
B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE = "b_virtualserver_modify_welcomemessage", /* Permission ID: 50 */
B_VIRTUALSERVER_MODIFY_MAXCLIENTS = "b_virtualserver_modify_maxclients", /* Permission ID: 51 */
B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS = "b_virtualserver_modify_reserved_slots", /* Permission ID: 52 */
B_VIRTUALSERVER_MODIFY_PASSWORD = "b_virtualserver_modify_password", /* Permission ID: 53 */
B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP = "b_virtualserver_modify_default_servergroup", /* Permission ID: 54 */
B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP = "b_virtualserver_modify_default_musicgroup", /* Permission ID: 55 */
B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP = "b_virtualserver_modify_default_channelgroup", /* Permission ID: 56 */
B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP = "b_virtualserver_modify_default_channeladmingroup", /* Permission ID: 57 */
B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE = "b_virtualserver_modify_channel_forced_silence", /* Permission ID: 58 */
B_VIRTUALSERVER_MODIFY_COMPLAIN = "b_virtualserver_modify_complain", /* Permission ID: 59 */
B_VIRTUALSERVER_MODIFY_ANTIFLOOD = "b_virtualserver_modify_antiflood", /* Permission ID: 60 */
B_VIRTUALSERVER_MODIFY_FT_SETTINGS = "b_virtualserver_modify_ft_settings", /* Permission ID: 61 */
B_VIRTUALSERVER_MODIFY_FT_QUOTAS = "b_virtualserver_modify_ft_quotas", /* Permission ID: 62 */
B_VIRTUALSERVER_MODIFY_HOSTMESSAGE = "b_virtualserver_modify_hostmessage", /* Permission ID: 63 */
B_VIRTUALSERVER_MODIFY_HOSTBANNER = "b_virtualserver_modify_hostbanner", /* Permission ID: 64 */
B_VIRTUALSERVER_MODIFY_HOSTBUTTON = "b_virtualserver_modify_hostbutton", /* Permission ID: 65 */
B_VIRTUALSERVER_MODIFY_PORT = "b_virtualserver_modify_port", /* Permission ID: 66 */
B_VIRTUALSERVER_MODIFY_HOST = "b_virtualserver_modify_host", /* Permission ID: 67 */
B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES = "b_virtualserver_modify_default_messages", /* Permission ID: 68 */
B_VIRTUALSERVER_MODIFY_AUTOSTART = "b_virtualserver_modify_autostart", /* Permission ID: 69 */
B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL = "b_virtualserver_modify_needed_identity_security_level", /* Permission ID: 70 */
B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR = "b_virtualserver_modify_priority_speaker_dimm_modificator", /* Permission ID: 71 */
B_VIRTUALSERVER_MODIFY_LOG_SETTINGS = "b_virtualserver_modify_log_settings", /* Permission ID: 72 */
B_VIRTUALSERVER_MODIFY_MIN_CLIENT_VERSION = "b_virtualserver_modify_min_client_version", /* Permission ID: 73 */
B_VIRTUALSERVER_MODIFY_ICON_ID = "b_virtualserver_modify_icon_id", /* Permission ID: 74 */
B_VIRTUALSERVER_MODIFY_WEBLIST = "b_virtualserver_modify_weblist", /* Permission ID: 75 */
B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE = "b_virtualserver_modify_codec_encryption_mode", /* Permission ID: 76 */
B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS = "b_virtualserver_modify_temporary_passwords", /* Permission ID: 77 */
B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS_OWN = "b_virtualserver_modify_temporary_passwords_own", /* Permission ID: 78 */
B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT = "b_virtualserver_modify_channel_temp_delete_delay_default", /* Permission ID: 79 */
B_VIRTUALSERVER_MODIFY_MUSIC_BOT_LIMIT = "b_virtualserver_modify_music_bot_limit", /* Permission ID: 80 */
I_CHANNEL_MIN_DEPTH = "i_channel_min_depth", /* Permission ID: 81 */
I_CHANNEL_MAX_DEPTH = "i_channel_max_depth", /* Permission ID: 82 */
B_CHANNEL_GROUP_INHERITANCE_END = "b_channel_group_inheritance_end", /* Permission ID: 83 */
I_CHANNEL_PERMISSION_MODIFY_POWER = "i_channel_permission_modify_power", /* Permission ID: 84 */
I_CHANNEL_NEEDED_PERMISSION_MODIFY_POWER = "i_channel_needed_permission_modify_power", /* Permission ID: 85 */
B_CHANNEL_INFO_VIEW = "b_channel_info_view", /* Permission ID: 86 */
B_CHANNEL_CREATE_CHILD = "b_channel_create_child", /* Permission ID: 87 */
B_CHANNEL_CREATE_PERMANENT = "b_channel_create_permanent", /* Permission ID: 88 */
B_CHANNEL_CREATE_SEMI_PERMANENT = "b_channel_create_semi_permanent", /* Permission ID: 89 */
B_CHANNEL_CREATE_TEMPORARY = "b_channel_create_temporary", /* Permission ID: 90 */
B_CHANNEL_CREATE_PRIVATE = "b_channel_create_private", /* Permission ID: 91 */
B_CHANNEL_CREATE_WITH_TOPIC = "b_channel_create_with_topic", /* Permission ID: 92 */
B_CHANNEL_CREATE_WITH_DESCRIPTION = "b_channel_create_with_description", /* Permission ID: 93 */
B_CHANNEL_CREATE_WITH_PASSWORD = "b_channel_create_with_password", /* Permission ID: 94 */
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8 = "b_channel_create_modify_with_codec_speex8", /* Permission ID: 95 */
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16 = "b_channel_create_modify_with_codec_speex16", /* Permission ID: 96 */
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32 = "b_channel_create_modify_with_codec_speex32", /* Permission ID: 97 */
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48 = "b_channel_create_modify_with_codec_celtmono48", /* Permission ID: 98 */
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE = "b_channel_create_modify_with_codec_opusvoice", /* Permission ID: 99 */
B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC = "b_channel_create_modify_with_codec_opusmusic", /* Permission ID: 100 */
I_CHANNEL_CREATE_MODIFY_WITH_CODEC_MAXQUALITY = "i_channel_create_modify_with_codec_maxquality", /* Permission ID: 101 */
I_CHANNEL_CREATE_MODIFY_WITH_CODEC_LATENCY_FACTOR_MIN = "i_channel_create_modify_with_codec_latency_factor_min", /* Permission ID: 102 */
B_CHANNEL_CREATE_WITH_MAXCLIENTS = "b_channel_create_with_maxclients", /* Permission ID: 103 */
B_CHANNEL_CREATE_WITH_MAXFAMILYCLIENTS = "b_channel_create_with_maxfamilyclients", /* Permission ID: 104 */
B_CHANNEL_CREATE_WITH_SORTORDER = "b_channel_create_with_sortorder", /* Permission ID: 105 */
B_CHANNEL_CREATE_WITH_DEFAULT = "b_channel_create_with_default", /* Permission ID: 106 */
B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER = "b_channel_create_with_needed_talk_power", /* Permission ID: 107 */
B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD = "b_channel_create_modify_with_force_password", /* Permission ID: 108 */
I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY = "i_channel_create_modify_with_temp_delete_delay", /* Permission ID: 109 */
B_CHANNEL_MODIFY_PARENT = "b_channel_modify_parent", /* Permission ID: 110 */
B_CHANNEL_MODIFY_MAKE_DEFAULT = "b_channel_modify_make_default", /* Permission ID: 111 */
B_CHANNEL_MODIFY_MAKE_PERMANENT = "b_channel_modify_make_permanent", /* Permission ID: 112 */
B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT = "b_channel_modify_make_semi_permanent", /* Permission ID: 113 */
B_CHANNEL_MODIFY_MAKE_TEMPORARY = "b_channel_modify_make_temporary", /* Permission ID: 114 */
B_CHANNEL_MODIFY_NAME = "b_channel_modify_name", /* Permission ID: 115 */
B_CHANNEL_MODIFY_TOPIC = "b_channel_modify_topic", /* Permission ID: 116 */
B_CHANNEL_MODIFY_DESCRIPTION = "b_channel_modify_description", /* Permission ID: 117 */
B_CHANNEL_MODIFY_PASSWORD = "b_channel_modify_password", /* Permission ID: 118 */
B_CHANNEL_MODIFY_CODEC = "b_channel_modify_codec", /* Permission ID: 119 */
B_CHANNEL_MODIFY_CODEC_QUALITY = "b_channel_modify_codec_quality", /* Permission ID: 120 */
B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR = "b_channel_modify_codec_latency_factor", /* Permission ID: 121 */
B_CHANNEL_MODIFY_MAXCLIENTS = "b_channel_modify_maxclients", /* Permission ID: 122 */
B_CHANNEL_MODIFY_MAXFAMILYCLIENTS = "b_channel_modify_maxfamilyclients", /* Permission ID: 123 */
B_CHANNEL_MODIFY_SORTORDER = "b_channel_modify_sortorder", /* Permission ID: 124 */
B_CHANNEL_MODIFY_NEEDED_TALK_POWER = "b_channel_modify_needed_talk_power", /* Permission ID: 125 */
I_CHANNEL_MODIFY_POWER = "i_channel_modify_power", /* Permission ID: 126 */
I_CHANNEL_NEEDED_MODIFY_POWER = "i_channel_needed_modify_power", /* Permission ID: 127 */
B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED = "b_channel_modify_make_codec_encrypted", /* Permission ID: 128 */
B_CHANNEL_MODIFY_TEMP_DELETE_DELAY = "b_channel_modify_temp_delete_delay", /* Permission ID: 129 */
B_CHANNEL_DELETE_PERMANENT = "b_channel_delete_permanent", /* Permission ID: 130 */
B_CHANNEL_DELETE_SEMI_PERMANENT = "b_channel_delete_semi_permanent", /* Permission ID: 131 */
B_CHANNEL_DELETE_TEMPORARY = "b_channel_delete_temporary", /* Permission ID: 132 */
B_CHANNEL_DELETE_FLAG_FORCE = "b_channel_delete_flag_force", /* Permission ID: 133 */
I_CHANNEL_DELETE_POWER = "i_channel_delete_power", /* Permission ID: 134 */
I_CHANNEL_NEEDED_DELETE_POWER = "i_channel_needed_delete_power", /* Permission ID: 135 */
B_CHANNEL_JOIN_PERMANENT = "b_channel_join_permanent", /* Permission ID: 136 */
B_CHANNEL_JOIN_SEMI_PERMANENT = "b_channel_join_semi_permanent", /* Permission ID: 137 */
B_CHANNEL_JOIN_TEMPORARY = "b_channel_join_temporary", /* Permission ID: 138 */
B_CHANNEL_JOIN_IGNORE_PASSWORD = "b_channel_join_ignore_password", /* Permission ID: 139 */
B_CHANNEL_JOIN_IGNORE_MAXCLIENTS = "b_channel_join_ignore_maxclients", /* Permission ID: 140 */
B_CHANNEL_IGNORE_VIEW_POWER = "b_channel_ignore_view_power", /* Permission ID: 141 */
I_CHANNEL_JOIN_POWER = "i_channel_join_power", /* Permission ID: 142 */
I_CHANNEL_NEEDED_JOIN_POWER = "i_channel_needed_join_power", /* Permission ID: 143 */
B_CHANNEL_IGNORE_JOIN_POWER = "b_channel_ignore_join_power", /* Permission ID: 144 */
I_CHANNEL_VIEW_POWER = "i_channel_view_power", /* Permission ID: 145 */
I_CHANNEL_NEEDED_VIEW_POWER = "i_channel_needed_view_power", /* Permission ID: 146 */
I_CHANNEL_SUBSCRIBE_POWER = "i_channel_subscribe_power", /* Permission ID: 147 */
I_CHANNEL_NEEDED_SUBSCRIBE_POWER = "i_channel_needed_subscribe_power", /* Permission ID: 148 */
I_CHANNEL_DESCRIPTION_VIEW_POWER = "i_channel_description_view_power", /* Permission ID: 149 */
I_CHANNEL_NEEDED_DESCRIPTION_VIEW_POWER = "i_channel_needed_description_view_power", /* Permission ID: 150 */
I_ICON_ID = "i_icon_id", /* Permission ID: 151 */
I_MAX_ICON_FILESIZE = "i_max_icon_filesize", /* Permission ID: 152 */
B_ICON_MANAGE = "b_icon_manage", /* Permission ID: 153 */
B_GROUP_IS_PERMANENT = "b_group_is_permanent", /* Permission ID: 154 */
I_GROUP_AUTO_UPDATE_TYPE = "i_group_auto_update_type", /* Permission ID: 155 */
I_GROUP_AUTO_UPDATE_MAX_VALUE = "i_group_auto_update_max_value", /* Permission ID: 156 */
I_GROUP_SORT_ID = "i_group_sort_id", /* Permission ID: 157 */
I_GROUP_SHOW_NAME_IN_TREE = "i_group_show_name_in_tree", /* Permission ID: 158 */
B_VIRTUALSERVER_SERVERGROUP_CREATE = "b_virtualserver_servergroup_create", /* Permission ID: 159 */
B_VIRTUALSERVER_SERVERGROUP_LIST = "b_virtualserver_servergroup_list", /* Permission ID: 160 */
B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST = "b_virtualserver_servergroup_permission_list", /* Permission ID: 161 */
B_VIRTUALSERVER_SERVERGROUP_CLIENT_LIST = "b_virtualserver_servergroup_client_list", /* Permission ID: 162 */
B_VIRTUALSERVER_CHANNELGROUP_CREATE = "b_virtualserver_channelgroup_create", /* Permission ID: 163 */
B_VIRTUALSERVER_CHANNELGROUP_LIST = "b_virtualserver_channelgroup_list", /* Permission ID: 164 */
B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST = "b_virtualserver_channelgroup_permission_list", /* Permission ID: 165 */
B_VIRTUALSERVER_CHANNELGROUP_CLIENT_LIST = "b_virtualserver_channelgroup_client_list", /* Permission ID: 166 */
B_VIRTUALSERVER_CLIENT_PERMISSION_LIST = "b_virtualserver_client_permission_list", /* Permission ID: 167 */
B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST = "b_virtualserver_channel_permission_list", /* Permission ID: 168 */
B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST = "b_virtualserver_channelclient_permission_list", /* Permission ID: 169 */
B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST = "b_virtualserver_playlist_permission_list", /* Permission ID: 170 */
I_SERVER_GROUP_MODIFY_POWER = "i_server_group_modify_power", /* Permission ID: 171 */
I_SERVER_GROUP_NEEDED_MODIFY_POWER = "i_server_group_needed_modify_power", /* Permission ID: 172 */
I_SERVER_GROUP_MEMBER_ADD_POWER = "i_server_group_member_add_power", /* Permission ID: 173 */
I_SERVER_GROUP_SELF_ADD_POWER = "i_server_group_self_add_power", /* Permission ID: 174 */
I_SERVER_GROUP_NEEDED_MEMBER_ADD_POWER = "i_server_group_needed_member_add_power", /* Permission ID: 175 */
I_SERVER_GROUP_MEMBER_REMOVE_POWER = "i_server_group_member_remove_power", /* Permission ID: 176 */
I_SERVER_GROUP_SELF_REMOVE_POWER = "i_server_group_self_remove_power", /* Permission ID: 177 */
I_SERVER_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_server_group_needed_member_remove_power", /* Permission ID: 178 */
I_CHANNEL_GROUP_MODIFY_POWER = "i_channel_group_modify_power", /* Permission ID: 179 */
I_CHANNEL_GROUP_NEEDED_MODIFY_POWER = "i_channel_group_needed_modify_power", /* Permission ID: 180 */
I_CHANNEL_GROUP_MEMBER_ADD_POWER = "i_channel_group_member_add_power", /* Permission ID: 181 */
I_CHANNEL_GROUP_SELF_ADD_POWER = "i_channel_group_self_add_power", /* Permission ID: 182 */
I_CHANNEL_GROUP_NEEDED_MEMBER_ADD_POWER = "i_channel_group_needed_member_add_power", /* Permission ID: 183 */
I_CHANNEL_GROUP_MEMBER_REMOVE_POWER = "i_channel_group_member_remove_power", /* Permission ID: 184 */
I_CHANNEL_GROUP_SELF_REMOVE_POWER = "i_channel_group_self_remove_power", /* Permission ID: 185 */
I_CHANNEL_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_channel_group_needed_member_remove_power", /* Permission ID: 186 */
I_GROUP_MEMBER_ADD_POWER = "i_group_member_add_power", /* Permission ID: 187 */
I_GROUP_NEEDED_MEMBER_ADD_POWER = "i_group_needed_member_add_power", /* Permission ID: 188 */
I_GROUP_MEMBER_REMOVE_POWER = "i_group_member_remove_power", /* Permission ID: 189 */
I_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_group_needed_member_remove_power", /* Permission ID: 190 */
I_GROUP_MODIFY_POWER = "i_group_modify_power", /* Permission ID: 191 */
I_GROUP_NEEDED_MODIFY_POWER = "i_group_needed_modify_power", /* Permission ID: 192 */
I_PERMISSION_MODIFY_POWER = "i_permission_modify_power", /* Permission ID: 193 */
B_PERMISSION_MODIFY_POWER_IGNORE = "b_permission_modify_power_ignore", /* Permission ID: 194 */
B_VIRTUALSERVER_SERVERGROUP_DELETE = "b_virtualserver_servergroup_delete", /* Permission ID: 195 */
B_VIRTUALSERVER_CHANNELGROUP_DELETE = "b_virtualserver_channelgroup_delete", /* Permission ID: 196 */
I_CLIENT_PERMISSION_MODIFY_POWER = "i_client_permission_modify_power", /* Permission ID: 197 */
I_CLIENT_NEEDED_PERMISSION_MODIFY_POWER = "i_client_needed_permission_modify_power", /* Permission ID: 198 */
I_CLIENT_MAX_CLONES_UID = "i_client_max_clones_uid", /* Permission ID: 199 */
I_CLIENT_MAX_CLONES_IP = "i_client_max_clones_ip", /* Permission ID: 200 */
I_CLIENT_MAX_CLONES_HWID = "i_client_max_clones_hwid", /* Permission ID: 201 */
I_CLIENT_MAX_IDLETIME = "i_client_max_idletime", /* Permission ID: 202 */
I_CLIENT_MAX_AVATAR_FILESIZE = "i_client_max_avatar_filesize", /* Permission ID: 203 */
I_CLIENT_MAX_CHANNEL_SUBSCRIPTIONS = "i_client_max_channel_subscriptions", /* Permission ID: 204 */
I_CLIENT_MAX_CHANNELS = "i_client_max_channels", /* Permission ID: 205 */
I_CLIENT_MAX_TEMPORARY_CHANNELS = "i_client_max_temporary_channels", /* Permission ID: 206 */
I_CLIENT_MAX_SEMI_CHANNELS = "i_client_max_semi_channels", /* Permission ID: 207 */
I_CLIENT_MAX_PERMANENT_CHANNELS = "i_client_max_permanent_channels", /* Permission ID: 208 */
B_CLIENT_USE_PRIORITY_SPEAKER = "b_client_use_priority_speaker", /* Permission ID: 209 */
B_CLIENT_SKIP_CHANNELGROUP_PERMISSIONS = "b_client_skip_channelgroup_permissions", /* Permission ID: 210 */
B_CLIENT_FORCE_PUSH_TO_TALK = "b_client_force_push_to_talk", /* Permission ID: 211 */
B_CLIENT_IGNORE_BANS = "b_client_ignore_bans", /* Permission ID: 212 */
B_CLIENT_IGNORE_VPN = "b_client_ignore_vpn", /* Permission ID: 213 */
B_CLIENT_IGNORE_ANTIFLOOD = "b_client_ignore_antiflood", /* Permission ID: 214 */
B_CLIENT_ENFORCE_VALID_HWID = "b_client_enforce_valid_hwid", /* Permission ID: 215 */
B_CLIENT_ALLOW_INVALID_PACKET = "b_client_allow_invalid_packet", /* Permission ID: 216 */
B_CLIENT_ALLOW_INVALID_BADGES = "b_client_allow_invalid_badges", /* Permission ID: 217 */
B_CLIENT_ISSUE_CLIENT_QUERY_COMMAND = "b_client_issue_client_query_command", /* Permission ID: 218 */
B_CLIENT_USE_RESERVED_SLOT = "b_client_use_reserved_slot", /* Permission ID: 219 */
B_CLIENT_USE_CHANNEL_COMMANDER = "b_client_use_channel_commander", /* Permission ID: 220 */
B_CLIENT_REQUEST_TALKER = "b_client_request_talker", /* Permission ID: 221 */
B_CLIENT_AVATAR_DELETE_OTHER = "b_client_avatar_delete_other", /* Permission ID: 222 */
B_CLIENT_IS_STICKY = "b_client_is_sticky", /* Permission ID: 223 */
B_CLIENT_IGNORE_STICKY = "b_client_ignore_sticky", /* Permission ID: 224 */
B_CLIENT_MUSIC_CREATE_PERMANENT = "b_client_music_create_permanent", /* Permission ID: 225 */
B_CLIENT_MUSIC_CREATE_SEMI_PERMANENT = "b_client_music_create_semi_permanent", /* Permission ID: 226 */
B_CLIENT_MUSIC_CREATE_TEMPORARY = "b_client_music_create_temporary", /* Permission ID: 227 */
B_CLIENT_MUSIC_MODIFY_PERMANENT = "b_client_music_modify_permanent", /* Permission ID: 228 */
B_CLIENT_MUSIC_MODIFY_SEMI_PERMANENT = "b_client_music_modify_semi_permanent", /* Permission ID: 229 */
B_CLIENT_MUSIC_MODIFY_TEMPORARY = "b_client_music_modify_temporary", /* Permission ID: 230 */
I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME = "i_client_music_create_modify_max_volume", /* Permission ID: 231 */
I_CLIENT_MUSIC_LIMIT = "i_client_music_limit", /* Permission ID: 232 */
I_CLIENT_MUSIC_NEEDED_DELETE_POWER = "i_client_music_needed_delete_power", /* Permission ID: 233 */
I_CLIENT_MUSIC_DELETE_POWER = "i_client_music_delete_power", /* Permission ID: 234 */
I_CLIENT_MUSIC_PLAY_POWER = "i_client_music_play_power", /* Permission ID: 235 */
I_CLIENT_MUSIC_NEEDED_PLAY_POWER = "i_client_music_needed_play_power", /* Permission ID: 236 */
I_CLIENT_MUSIC_MODIFY_POWER = "i_client_music_modify_power", /* Permission ID: 237 */
I_CLIENT_MUSIC_NEEDED_MODIFY_POWER = "i_client_music_needed_modify_power", /* Permission ID: 238 */
I_CLIENT_MUSIC_RENAME_POWER = "i_client_music_rename_power", /* Permission ID: 239 */
I_CLIENT_MUSIC_NEEDED_RENAME_POWER = "i_client_music_needed_rename_power", /* Permission ID: 240 */
B_PLAYLIST_CREATE = "b_playlist_create", /* Permission ID: 241 */
I_PLAYLIST_VIEW_POWER = "i_playlist_view_power", /* Permission ID: 242 */
I_PLAYLIST_NEEDED_VIEW_POWER = "i_playlist_needed_view_power", /* Permission ID: 243 */
I_PLAYLIST_MODIFY_POWER = "i_playlist_modify_power", /* Permission ID: 244 */
I_PLAYLIST_NEEDED_MODIFY_POWER = "i_playlist_needed_modify_power", /* Permission ID: 245 */
I_PLAYLIST_PERMISSION_MODIFY_POWER = "i_playlist_permission_modify_power", /* Permission ID: 246 */
I_PLAYLIST_NEEDED_PERMISSION_MODIFY_POWER = "i_playlist_needed_permission_modify_power", /* Permission ID: 247 */
I_PLAYLIST_DELETE_POWER = "i_playlist_delete_power", /* Permission ID: 248 */
I_PLAYLIST_NEEDED_DELETE_POWER = "i_playlist_needed_delete_power", /* Permission ID: 249 */
I_PLAYLIST_SONG_ADD_POWER = "i_playlist_song_add_power", /* Permission ID: 250 */
I_PLAYLIST_SONG_NEEDED_ADD_POWER = "i_playlist_song_needed_add_power", /* Permission ID: 251 */
I_PLAYLIST_SONG_REMOVE_POWER = "i_playlist_song_remove_power", /* Permission ID: 254 */
I_PLAYLIST_SONG_NEEDED_REMOVE_POWER = "i_playlist_song_needed_remove_power", /* Permission ID: 255 */
B_CLIENT_INFO_VIEW = "b_client_info_view", /* Permission ID: 256 */
B_CLIENT_PERMISSIONOVERVIEW_VIEW = "b_client_permissionoverview_view", /* Permission ID: 257 */
B_CLIENT_PERMISSIONOVERVIEW_OWN = "b_client_permissionoverview_own", /* Permission ID: 258 */
B_CLIENT_REMOTEADDRESS_VIEW = "b_client_remoteaddress_view", /* Permission ID: 259 */
I_CLIENT_SERVERQUERY_VIEW_POWER = "i_client_serverquery_view_power", /* Permission ID: 260 */
I_CLIENT_NEEDED_SERVERQUERY_VIEW_POWER = "i_client_needed_serverquery_view_power", /* Permission ID: 261 */
B_CLIENT_CUSTOM_INFO_VIEW = "b_client_custom_info_view", /* Permission ID: 262 */
B_CLIENT_MUSIC_CHANNEL_LIST = "b_client_music_channel_list", /* Permission ID: 263 */
B_CLIENT_MUSIC_SERVER_LIST = "b_client_music_server_list", /* Permission ID: 264 */
I_CLIENT_MUSIC_INFO = "i_client_music_info", /* Permission ID: 265 */
I_CLIENT_MUSIC_NEEDED_INFO = "i_client_music_needed_info", /* Permission ID: 266 */
I_CLIENT_KICK_FROM_SERVER_POWER = "i_client_kick_from_server_power", /* Permission ID: 267 */
I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER = "i_client_needed_kick_from_server_power", /* Permission ID: 268 */
I_CLIENT_KICK_FROM_CHANNEL_POWER = "i_client_kick_from_channel_power", /* Permission ID: 269 */
I_CLIENT_NEEDED_KICK_FROM_CHANNEL_POWER = "i_client_needed_kick_from_channel_power", /* Permission ID: 270 */
I_CLIENT_BAN_POWER = "i_client_ban_power", /* Permission ID: 271 */
I_CLIENT_NEEDED_BAN_POWER = "i_client_needed_ban_power", /* Permission ID: 272 */
I_CLIENT_MOVE_POWER = "i_client_move_power", /* Permission ID: 273 */
I_CLIENT_NEEDED_MOVE_POWER = "i_client_needed_move_power", /* Permission ID: 274 */
I_CLIENT_COMPLAIN_POWER = "i_client_complain_power", /* Permission ID: 275 */
I_CLIENT_NEEDED_COMPLAIN_POWER = "i_client_needed_complain_power", /* Permission ID: 276 */
B_CLIENT_COMPLAIN_LIST = "b_client_complain_list", /* Permission ID: 277 */
B_CLIENT_COMPLAIN_DELETE_OWN = "b_client_complain_delete_own", /* Permission ID: 278 */
B_CLIENT_COMPLAIN_DELETE = "b_client_complain_delete", /* Permission ID: 279 */
B_CLIENT_BAN_LIST = "b_client_ban_list", /* Permission ID: 280 */
B_CLIENT_BAN_LIST_GLOBAL = "b_client_ban_list_global", /* Permission ID: 281 */
B_CLIENT_BAN_TRIGGER_LIST = "b_client_ban_trigger_list", /* Permission ID: 282 */
B_CLIENT_BAN_CREATE = "b_client_ban_create", /* Permission ID: 283 */
B_CLIENT_BAN_CREATE_GLOBAL = "b_client_ban_create_global", /* Permission ID: 284 */
B_CLIENT_BAN_NAME = "b_client_ban_name", /* Permission ID: 285 */
B_CLIENT_BAN_IP = "b_client_ban_ip", /* Permission ID: 286 */
B_CLIENT_BAN_HWID = "b_client_ban_hwid", /* Permission ID: 287 */
B_CLIENT_BAN_EDIT = "b_client_ban_edit", /* Permission ID: 288 */
B_CLIENT_BAN_EDIT_GLOBAL = "b_client_ban_edit_global", /* Permission ID: 289 */
B_CLIENT_BAN_DELETE_OWN = "b_client_ban_delete_own", /* Permission ID: 290 */
B_CLIENT_BAN_DELETE = "b_client_ban_delete", /* Permission ID: 291 */
B_CLIENT_BAN_DELETE_OWN_GLOBAL = "b_client_ban_delete_own_global", /* Permission ID: 292 */
B_CLIENT_BAN_DELETE_GLOBAL = "b_client_ban_delete_global", /* Permission ID: 293 */
I_CLIENT_BAN_MAX_BANTIME = "i_client_ban_max_bantime", /* Permission ID: 294 */
I_CLIENT_PRIVATE_TEXTMESSAGE_POWER = "i_client_private_textmessage_power", /* Permission ID: 295 */
I_CLIENT_NEEDED_PRIVATE_TEXTMESSAGE_POWER = "i_client_needed_private_textmessage_power", /* Permission ID: 296 */
B_CLIENT_EVEN_TEXTMESSAGE_SEND = "b_client_even_textmessage_send", /* Permission ID: 297 */
B_CLIENT_SERVER_TEXTMESSAGE_SEND = "b_client_server_textmessage_send", /* Permission ID: 298 */
B_CLIENT_CHANNEL_TEXTMESSAGE_SEND = "b_client_channel_textmessage_send", /* Permission ID: 299 */
B_CLIENT_OFFLINE_TEXTMESSAGE_SEND = "b_client_offline_textmessage_send", /* Permission ID: 300 */
I_CLIENT_TALK_POWER = "i_client_talk_power", /* Permission ID: 301 */
I_CLIENT_NEEDED_TALK_POWER = "i_client_needed_talk_power", /* Permission ID: 302 */
I_CLIENT_POKE_POWER = "i_client_poke_power", /* Permission ID: 303 */
I_CLIENT_NEEDED_POKE_POWER = "i_client_needed_poke_power", /* Permission ID: 304 */
B_CLIENT_SET_FLAG_TALKER = "b_client_set_flag_talker", /* Permission ID: 305 */
I_CLIENT_WHISPER_POWER = "i_client_whisper_power", /* Permission ID: 306 */
I_CLIENT_NEEDED_WHISPER_POWER = "i_client_needed_whisper_power", /* Permission ID: 307 */
B_CLIENT_MODIFY_DESCRIPTION = "b_client_modify_description", /* Permission ID: 308 */
B_CLIENT_MODIFY_OWN_DESCRIPTION = "b_client_modify_own_description", /* Permission ID: 309 */
B_CLIENT_USE_BBCODE_ANY = "b_client_use_bbcode_any", /* Permission ID: 310 */
B_CLIENT_USE_BBCODE_URL = "b_client_use_bbcode_url", /* Permission ID: 311 */
B_CLIENT_USE_BBCODE_IMAGE = "b_client_use_bbcode_image", /* Permission ID: 312 */
B_CLIENT_MODIFY_DBPROPERTIES = "b_client_modify_dbproperties", /* Permission ID: 313 */
B_CLIENT_DELETE_DBPROPERTIES = "b_client_delete_dbproperties", /* Permission ID: 314 */
B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN = "b_client_create_modify_serverquery_login", /* Permission ID: 315 */
B_CLIENT_QUERY_CREATE = "b_client_query_create", /* Permission ID: 316 */
B_CLIENT_QUERY_LIST = "b_client_query_list", /* Permission ID: 317 */
B_CLIENT_QUERY_LIST_OWN = "b_client_query_list_own", /* Permission ID: 318 */
B_CLIENT_QUERY_RENAME = "b_client_query_rename", /* Permission ID: 319 */
B_CLIENT_QUERY_RENAME_OWN = "b_client_query_rename_own", /* Permission ID: 320 */
B_CLIENT_QUERY_CHANGE_PASSWORD = "b_client_query_change_password", /* Permission ID: 321 */
B_CLIENT_QUERY_CHANGE_OWN_PASSWORD = "b_client_query_change_own_password", /* Permission ID: 322 */
B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL = "b_client_query_change_password_global", /* Permission ID: 323 */
B_CLIENT_QUERY_DELETE = "b_client_query_delete", /* Permission ID: 324 */
B_CLIENT_QUERY_DELETE_OWN = "b_client_query_delete_own", /* Permission ID: 325 */
B_FT_IGNORE_PASSWORD = "b_ft_ignore_password", /* Permission ID: 326 */
B_FT_TRANSFER_LIST = "b_ft_transfer_list", /* Permission ID: 327 */
I_FT_FILE_UPLOAD_POWER = "i_ft_file_upload_power", /* Permission ID: 328 */
I_FT_NEEDED_FILE_UPLOAD_POWER = "i_ft_needed_file_upload_power", /* Permission ID: 329 */
I_FT_FILE_DOWNLOAD_POWER = "i_ft_file_download_power", /* Permission ID: 330 */
I_FT_NEEDED_FILE_DOWNLOAD_POWER = "i_ft_needed_file_download_power", /* Permission ID: 331 */
I_FT_FILE_DELETE_POWER = "i_ft_file_delete_power", /* Permission ID: 332 */
I_FT_NEEDED_FILE_DELETE_POWER = "i_ft_needed_file_delete_power", /* Permission ID: 333 */
I_FT_FILE_RENAME_POWER = "i_ft_file_rename_power", /* Permission ID: 334 */
I_FT_NEEDED_FILE_RENAME_POWER = "i_ft_needed_file_rename_power", /* Permission ID: 335 */
I_FT_FILE_BROWSE_POWER = "i_ft_file_browse_power", /* Permission ID: 336 */
I_FT_NEEDED_FILE_BROWSE_POWER = "i_ft_needed_file_browse_power", /* Permission ID: 337 */
I_FT_DIRECTORY_CREATE_POWER = "i_ft_directory_create_power", /* Permission ID: 338 */
I_FT_NEEDED_DIRECTORY_CREATE_POWER = "i_ft_needed_directory_create_power", /* Permission ID: 339 */
I_FT_QUOTA_MB_DOWNLOAD_PER_CLIENT = "i_ft_quota_mb_download_per_client", /* Permission ID: 340 */
I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client", /* Permission ID: 341 */
}
class PermissionInfo {
@ -350,6 +404,7 @@ class ChannelPermissionRequest {
class TeaPermissionRequest {
client_id?: number;
channel_id?: number;
playlist_id?: number;
promise: LaterPromise<PermissionValue[]>;
}
@ -363,6 +418,7 @@ class PermissionManager {
requests_channel_permissions: ChannelPermissionRequest[] = [];
requests_client_permissions: TeaPermissionRequest[] = [];
requests_client_channel_permissions: TeaPermissionRequest[] = [];
requests_playlist_permissions: TeaPermissionRequest[] = [];
initializedListener: ((initialized: boolean) => void)[] = [];
private _cacheNeededPermissions: any;
@ -443,8 +499,10 @@ class PermissionManager {
this.handle.serverConnection.commandHandler["notifyclientneededpermissions"] = this.onNeededPermissions.bind(this);
this.handle.serverConnection.commandHandler["notifypermissionlist"] = this.onPermissionList.bind(this);
this.handle.serverConnection.commandHandler["notifychannelpermlist"] = this.onChannelPermList.bind(this);
this.handle.serverConnection.commandHandler["notifyclientpermlist"] = this.onClientPermList.bind(this);
this.handle.serverConnection.commandHandler["notifyplaylistpermlist"] = this.onPlaylistPermList.bind(this);
}
initialized() : boolean {
@ -588,6 +646,17 @@ class PermissionManager {
});
}
private onClientPermList(json: any[]) {
let client = parseInt(json[0]["cldbid"]);
let permissions = PermissionManager.parse_permission_bulk(json, this);
for(let req of this.requests_client_permissions.slice(0)) {
if(req.client_id == client) {
this.requests_client_permissions.remove(req);
req.promise.resolved(permissions);
}
}
}
requestClientPermissions(client_id: number) : Promise<PermissionValue[]> {
for(let request of this.requests_client_permissions)
if(request.client_id == client_id && request.promise.time() + 1000 > Date.now())
@ -598,7 +667,7 @@ class PermissionManager {
request.promise = new LaterPromise<PermissionValue[]>();
this.handle.serverConnection.sendCommand("clientpermlist", {cldbid: client_id}).catch(error => {
if(error instanceof CommandResult && error.id == 0x0501)
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT)
request.promise.resolved([]);
else
request.promise.rejected(error);
@ -619,7 +688,7 @@ class PermissionManager {
request.promise = new LaterPromise<PermissionValue[]>();
this.handle.serverConnection.sendCommand("channelclientpermlist", {cldbid: client_id, cid: channel_id}).catch(error => {
if(error instanceof CommandResult && error.id == 0x0501)
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT)
request.promise.resolved([]);
else
request.promise.rejected(error);
@ -629,17 +698,39 @@ class PermissionManager {
return request.promise;
}
private onClientPermList(json: any[]) {
let client = parseInt(json[0]["cldbid"]);
private onPlaylistPermList(json: any[]) {
let playlist_id = parseInt(json[0]["playlist_id"]);
let permissions = PermissionManager.parse_permission_bulk(json, this);
for(let req of this.requests_client_permissions.slice(0)) {
if(req.client_id == client) {
this.requests_client_permissions.remove(req);
for(let req of this.requests_playlist_permissions.slice(0)) {
if(req.playlist_id == playlist_id) {
this.requests_playlist_permissions.remove(req);
req.promise.resolved(permissions);
}
}
}
requestPlaylistPermissions(playlist_id: number) : Promise<PermissionValue[]> {
for(let request of this.requests_playlist_permissions)
if(request.playlist_id == playlist_id && request.promise.time() + 1000 > Date.now())
return request.promise;
let request: TeaPermissionRequest = {} as any;
request.playlist_id = playlist_id;
request.promise = new LaterPromise<PermissionValue[]>();
this.handle.serverConnection.sendCommand("playlistpermlist", {playlist_id: playlist_id}).catch(error => {
if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT)
request.promise.resolved([]);
else
request.promise.rejected(error);
});
this.requests_playlist_permissions.push(request);
return request.promise;
}
neededPermission(key: number | string | PermissionType | PermissionInfo) : PermissionValue {
for(let perm of this.neededPermissions)
if(perm.type.id == key || perm.type.name == key || perm.type == key)
@ -693,4 +784,20 @@ class PermissionManager {
return result;
}
/**
* Generates an enum with all know permission types, used for the enum above
*/
export_permission_types() {
let result = "";
result = result + "enum PermissionType {\n";
for(const permission of this.permissionList) {
if(!permission.name) continue;
result = result + "\t" + permission.name.toUpperCase() + " = \"" + permission.name.toLowerCase() + "\", /* Permission ID: " + permission.id + " */\n";
}
result = result + "}";
return result;
}
}

View File

@ -129,6 +129,7 @@ if(typeof ($) !== "undefined") {
}
if(!$.fn.hasScrollBar)
$.fn.hasScrollBar = function() {
if(this.length <= 0) return false;
return this.get(0).scrollHeight > this.height();
}

View File

@ -433,14 +433,13 @@ class ChannelEntry {
name: tr("Create music bot"),
callback: () => {
this.channelTree.client.serverConnection.sendCommand("musicbotcreate", {cid: this.channelId}).then(() => {
createInfoModal(tr("Bot successfully created"), tr("But has been successfully created.")).open();
createInfoModal(tr("Bot successfully created"), tr("Bot has been successfully created.")).open();
}).catch(error => {
if(error instanceof CommandResult) {
error = error.extra_message || error.message;
}
//TODO tr
createErrorModal(tr("Failed to create bot"), "Failed to create the music bot:<br>" + error).open();
createErrorModal(tr("Failed to create bot"), MessageHelper.formatMessage(tr("Failed to create the music bot:<br>{0}"), error)).open();
});
}
},
@ -583,26 +582,16 @@ class ChannelEntry {
tag.addClass("client-channel_" + type + "_subscribed");
}
createChatTag(braces: boolean = false) : JQuery {
let tag = $.spawn("div");
generate_bbcode() {
return "[url=channel://" + this.channelId + "/" + encodeURIComponent(this.properties.channel_name) + "]" + this.formatedChannelName() + "[/url]";
}
tag.css("display", "inline-block");
tag.css("cursor", "pointer");
tag.css("font-weight", "bold");
tag.css("color", "darkblue");
if(braces)
tag.text("\"" + this.channelName() + "\"");
else
tag.text(this.channelName());
tag.contextmenu(event => {
if(event.isDefaultPrevented()) return;
event.preventDefault();
this.showContextMenu(event.pageX, event.pageY);
});
tag.attr("channelId", this.channelId);
tag.attr("channelName", this.channelName());
return tag;
generate_tag(braces: boolean = false) : JQuery {
return $(htmltags.generate_channel({
channel_name: this.properties.channel_name,
channel_id: this.channelId,
add_braces: braces
}));
}
channelType() : ChannelType {

View File

@ -130,8 +130,13 @@ class ClientEntry {
if(event.which != 1) return; //Only the left button
let clients = this.channelTree.currently_selected as (ClientEntry | ClientEntry[]);
if(clients != this && !($.isArray(clients) && clients.indexOf(this) != -1))
clients = $.isArray(clients) ? [...clients, this] : [clients, this];
if(ppt.key_pressed(ppt.SpecialKey.SHIFT)) {
if(clients != this && !($.isArray(clients) && clients.indexOf(this) != -1))
clients = $.isArray(clients) ? [...clients, this] : [clients, this];
} else {
clients = this;
}
this.channelTree.client_mover.activate(clients, target => {
if(!target) return;
@ -435,33 +440,21 @@ class ClientEntry {
return this._tag;
}
static bbcodeTag(id: number, name: string, uid: string) : string {
return "[url=client://" + id + "/" + uid + "~" + encodeURIComponent(name) + "]" + name + "[/url]";
}
static chatTag(id: number, name: string, uid: string, braces: boolean = false) : JQuery {
let tag = $.spawn("div");
return $(htmltags.generate_client({
client_name: name,
client_id: id,
client_unique_id: uid,
add_braces: braces
}));
}
tag.css("cursor", "pointer")
.css("font-weight", "bold")
.css("color", "darkblue")
.css("display", "inline-block")
.css("margin", 0);
if(braces)
tag.text("\"" + name + "\"");
else
tag.text(name);
tag.contextmenu(event => {
if(event.isDefaultPrevented()) return;
event.preventDefault();
let client = globalClient.channelTree.findClient(id);
if(!client) return;
if(client.properties.client_unique_identifier != uid) return;
client.showContextMenu(event.pageX, event.pageY);
});
tag.attr("clientId", id);
tag.attr("clientUid", uid);
tag.attr("clientName", name);
return tag;
create_bbcode() : string {
return ClientEntry.bbcodeTag(this.clientId(), this.clientNickName(), this.clientUid());
}
createChatTag(braces: boolean = false) : JQuery {
@ -832,6 +825,9 @@ class LocalClientEntry extends ClientEntry {
class MusicClientProperties extends ClientProperties {
player_state: number = 0;
player_volume: number = 0;
client_playlist_id: number = 0;
client_disabled: boolean = false;
}
class MusicClientPlayerInfo {
@ -903,13 +899,36 @@ class MusicClientEntry extends ClientEntry {
}, { width: 400, maxLength: 255 }).open();
},
type: MenuEntryType.ENTRY
}, {
},
/*
{
name: tr("Open music panel"),
icon: "client-edit",
disabled: true,
callback: () => {},
type: MenuEntryType.ENTRY
}, {
},
*/
{
name: tr("Open bot's playlist"),
icon: "client-edit",
disabled: false,
callback: () => {
this.channelTree.client.serverConnection.helper.request_playlist_list().then(lists => {
for(const entry of lists) {
if(entry.playlist_id == this.properties.client_playlist_id) {
Modals.spawnPlaylistEdit(this.channelTree.client, entry);
return;
}
}
createErrorModal(tr("Invalid permissions"), tr("You dont have to see the bots playlist.")).open();
}).catch(error => {
createErrorModal(tr("Failed to query playlist."), tr("Failed to query playlist info.")).open();
});
},
type: MenuEntryType.ENTRY
},
{
name: tr("Quick url replay"),
icon: "client-edit",
disabled: false,
@ -965,13 +984,33 @@ class MusicClientEntry extends ClientEntry {
{
type: MenuEntryType.ENTRY,
icon: "client-volume",
name: tr("Change Volume"),
name: tr("Change local volume"),
callback: () => {
Modals.spawnChangeVolume(this.audioController.volume, volume => {
settings.changeServer("volume_client_" + this.clientUid(), volume);
this.audioController.volume = volume;
if(globalClient.selectInfo.currentSelected == this)
globalClient.selectInfo.update();
(<MusicInfoManager>globalClient.selectInfo.current_manager()).update_local_volume(volume);
});
}
},
{
type: MenuEntryType.ENTRY,
icon: "client-volume",
name: tr("Change remote volume"),
callback: () => {
let max_volume = this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME).value;
if(max_volume < 0)
max_volume = 100;
Modals.spawnChangeRemoteVolume(this.properties.player_volume, max_volume / 100, value => {
this.channelTree.client.serverConnection.sendCommand("clientedit", {
clid: this.clientId(),
player_volume: value,
}).then(() => {
if(globalClient.selectInfo.currentSelected == this)
(<MusicInfoManager>globalClient.selectInfo.current_manager()).update_remote_volume(value);
});
});
}
},

View File

@ -24,18 +24,22 @@ class ClientMover {
private hover_text() {
if($.isArray(this.selected_client)) {
let result = "";
for(const client of this.selected_client)
result = result + ", " + client.clientNickName();
if(result.length < 2)
return result;
return result.substr(2);
return this.selected_client.filter(client => !!client).map(client => client.clientNickName()).join(", ");
} else if(this.selected_client) {
return (<ClientEntry>this.selected_client).clientNickName();
} else
return "";
}
private bbcode_text() {
if($.isArray(this.selected_client)) {
return this.selected_client.filter(client => !!client).map(client => client.create_bbcode()).join(", ");
} else if(this.selected_client) {
return (<ClientEntry>this.selected_client).create_bbcode();
} else
return "";
}
activate(client: ClientEntry | ClientEntry[], callback: (channel?: ChannelEntry) => any, event: any) {
this.finish_listener(undefined);
@ -126,6 +130,21 @@ class ClientMover {
}
this.callback = undefined;
}
/* test for the chat box */
{
const elements = document.elementsFromPoint(event.pageX, event.pageY);
console.error(elements);
while(elements.length > 0) {
if(elements[0].classList.contains("client-chat-box-field")) break;
elements.pop_front();
}
if(elements.length > 0) {
const element = $(<HTMLTextAreaElement>elements[0]);
element.val((element.val() || "") + this.bbcode_text());
}
}
}
deactivate() {

View File

@ -42,6 +42,7 @@ class ControlBar {
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));
this.htmlTag.find(".button-playlist-manage").on('click', this.on_playlist_manage.bind(this));
{
let tokens = this.htmlTag.find(".btn_token");
tokens.find(".button-dropdown").on('click', () => {
@ -454,4 +455,12 @@ class ControlBar {
createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open();
}
}
private on_playlist_manage() {
if(this.handle && this.handle.connected) {
Modals.spawnPlaylistManage(this.handle);
} else {
createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open();
}
}
}

View File

@ -57,7 +57,7 @@ class InfoBar<AvailableTypes = ServerEntry | ChannelEntry | ClientEntry | undefi
private _tag_info: JQuery<HTMLElement>;
private _tag_banner: JQuery<HTMLElement>;
private current_manager: InfoManagerBase = undefined;
private _current_manager: InfoManagerBase = undefined;
private managers: InfoManagerBase[] = [];
private banner_manager: Hostbanner;
@ -77,9 +77,9 @@ class InfoBar<AvailableTypes = ServerEntry | ChannelEntry | ClientEntry | undefi
setCurrentSelected(entry: AvailableTypes) {
if(this.current_selected == entry) return;
if(this.current_manager) {
(this.current_manager as InfoManager<AvailableTypes>).finalizeFrame(this.current_selected, this._tag_info);
this.current_manager = null;
if(this._current_manager) {
(this._current_manager as InfoManager<AvailableTypes>).finalizeFrame(this.current_selected, this._tag_info);
this._current_manager = null;
this.current_selected = null;
}
this._tag_info.empty();
@ -87,14 +87,14 @@ class InfoBar<AvailableTypes = ServerEntry | ChannelEntry | ClientEntry | undefi
this.current_selected = entry;
for(let manager of this.managers) {
if(manager.available(this.current_selected)) {
this.current_manager = manager;
this._current_manager = manager;
break;
}
}
console.log(tr("Using info manager: %o"), this.current_manager);
if(this.current_manager)
(this.current_manager as InfoManager<AvailableTypes>).createFrame(this, this.current_selected, this._tag_info);
console.log(tr("Using info manager: %o"), this._current_manager);
if(this._current_manager)
(this._current_manager as InfoManager<AvailableTypes>).createFrame(this, this.current_selected, this._tag_info);
}
get currentSelected() {
@ -102,13 +102,17 @@ class InfoBar<AvailableTypes = ServerEntry | ChannelEntry | ClientEntry | undefi
}
update(){
if(this.current_manager && this.current_selected)
(this.current_manager as InfoManager<AvailableTypes>).updateFrame(this.current_selected, this._tag_info);
if(this._current_manager && this.current_selected)
(this._current_manager as InfoManager<AvailableTypes>).updateFrame(this.current_selected, this._tag_info);
}
update_banner() {
this.banner_manager.update();
}
current_manager() { return this._current_manager; }
html_tag() { return this._htmlTag; }
}
class Hostbanner {
@ -302,7 +306,7 @@ class ChannelInfoManager extends InfoManager<ChannelEntry> {
html_tag.empty();
let properties: any = {};
properties["channel_name"] = channel.createChatTag();
properties["channel_name"] = channel.generate_tag(false);
properties["channel_type"] = ChannelType.normalize(channel.channelType());
properties["channel_clients"] = channel.channelTree.clientsByChannel(channel).length;
properties["channel_subscribed"] = true; //TODO
@ -634,6 +638,18 @@ class MusicInfoManager extends ClientInfoManager {
});
console.log("Transform: " + transform);
}
this.registerInterval(setInterval(() => {
html_tag.find(".update_onlinetime").text(formatDate(bot.calculateOnlineTime()));
}, 1000));
}
update_local_volume(volume: number) {
this.handle.html_tag().find(".property-volume-local").text(Math.floor(volume * 100) + "%");
}
update_remote_volume(volume: number) {
this.handle.html_tag().find(".property-volume-remote").text(Math.floor(volume * 100) + "%")
}
available<V>(object: V): boolean {

223
shared/js/ui/htmltags.ts Normal file
View File

@ -0,0 +1,223 @@
namespace htmltags {
let mouse_coordinates: {x: number, y: number} = {x: 0, y: 0};
function initialize() {
document.addEventListener('mousemove', event => {
mouse_coordinates.x = event.pageX;
mouse_coordinates.y = event.pageY;
});
}
initialize();
export interface ClientProperties {
client_id: number,
client_unique_id: string,
client_name: string,
add_braces?: boolean
}
export interface ChannelProperties {
channel_id: number,
channel_name: string,
channel_display_name?: string,
add_braces?: boolean
}
/* required for the bbcodes */
function generate_client_open(properties: ClientProperties) : string {
let result = "";
/* build the opening tag: <div ...> */
result = result + "<div class='htmltag-client' ";
if(properties.client_id)
result = result + "client-id='" + properties.client_id + "' ";
if(properties.client_unique_id && properties.client_unique_id != "unknown")
result = result + "client-unique-id='" + encodeURIComponent(properties.client_unique_id) + "' ";
if(properties.client_name)
result = result + "client-name='" + encodeURIComponent(properties.client_name) + "' ";
/* add the click handler */
result += "oncontextmenu='return htmltags.callbacks.callback_context_client($(this));'";
result = result + ">";
return result;
}
export function generate_client(properties: ClientProperties) : string {
let result = generate_client_open(properties);
/* content */
{
if(properties.add_braces)
result = result + "\"";
result = result + MessageHelper.htmlEscape(properties.client_name || "undefined").join(" ");
if(properties.add_braces)
result = result + "\"";
}
/* close tag */
{
result += "</div>";
}
return result;
}
/* required for the bbcodes */
function generate_channel_open(properties: ChannelProperties) : string {
let result = "";
/* build the opening tag: <div ...> */
result = result + "<div class='htmltag-channel' ";
if(properties.channel_id)
result = result + "channel-id='" + properties.channel_id + "' ";
if(properties.channel_name)
result = result + "channel-name='" + encodeURIComponent(properties.channel_name) + "' ";
/* add the click handler */
result += "oncontextmenu='return htmltags.callbacks.callback_context_channel($(this));'";
result = result + ">";
return result;
}
export function generate_channel(properties: ChannelProperties) : string {
let result = generate_channel_open(properties);
/* content */
{
if(properties.add_braces)
result = result + "\"";
result = result + MessageHelper.htmlEscape(properties.channel_display_name || properties.channel_name || "undefined").join(" ");
if(properties.add_braces)
result = result + "\"";
}
/* close tag */
{
result += "</div>";
}
return result;
}
export namespace callbacks {
export function callback_context_client(element: JQuery) {
const client_id = parseInt(element.attr("client-id") || "0");
const client_unique_id = decodeURIComponent(element.attr("client-unique-id") || "");
/* we ignore the name, we cant find clients by name because the name is too volatile*/
let client: ClientEntry;
if(globalClient && globalClient.channelTree) {
if(!client && client_id) {
client = globalClient.channelTree.findClient(client_id);
if(client && (client_unique_id && client.properties.client_unique_identifier != client_unique_id)) {
client = undefined; /* client id dosn't match anymore, lets search for the unique id */
}
}
if(!client && client_unique_id)
client = globalClient.channelTree.find_client_by_unique_id(client_unique_id);
}
if(!client) {
/* we may should open a "offline" menu? */
log.debug(LogCategory.GENERAL, "Failed to resolve client from html tag. Client id: %o, Client unique id: %o, Client name: %o",
client_id,
client_unique_id,
decodeURIComponent(element.attr("client-name"))
);
return false;
}
client.showContextMenu(mouse_coordinates.x, mouse_coordinates.y);
return false;
}
export function callback_context_channel(element: JQuery) {
const channel_id = parseInt(element.attr("channel-id") || "0");
let channel: ChannelEntry;
if(globalClient && globalClient.channelTree) {
channel = globalClient.channelTree.findChannel(channel_id);
}
if(!channel)
return false;
channel.showContextMenu(mouse_coordinates.x, mouse_coordinates.y);
return false;
}
}
namespace bbcodes {
/* the = because we sometimes get that */
//const url_client_regex = /(?:=)?client:\/\/(?<client_id>[0-9]+)\/(?<client_unique_id>[a-zA-Z0-9+=#]+)~(?<client_name>(?:[^%]|%[0-9A-Fa-f]{2})+)$/g;
const url_client_regex = /(?:=)?client:\/\/([0-9]+)\/([a-zA-Z0-9+=/#]+)~((?:[^%]|%[0-9A-Fa-f]{2})+)$/g; /* IDK which browsers already support group naming */
const url_channel_regex = /(?:=)?channel:\/\/([0-9]+)~((?:[^%]|%[0-9A-Fa-f]{2})+)$/g;
function initialize() {
const origin_url = XBBCODE.tags()["url"];
XBBCODE.addTags({
function: {
openTag: (params, content) => {
if(params.match(url_channel_regex)) {
const groups = url_channel_regex.exec(params);
return generate_channel_open({
add_braces: false,
channel_id: parseInt(groups[1]),
channel_name: decodeURIComponent(groups[2])
});
} else if(params.match(url_client_regex)) {
const groups = url_client_regex.exec(params);
return generate_client_open({
add_braces: false,
client_id: parseInt(groups[1]),
client_unique_id: groups[2],
client_name: decodeURIComponent(groups[3])
});
}
return origin_url.openTag(params, content);
},
closeTag: (params, content) => {
if(params.match(url_client_regex))
return "</div>";
if(params.match(url_channel_regex))
return "</div>";
return origin_url.closeTag(params, content);
}
},
tag: "url"
})
/*
"img": {
openTag: function(params,content) {
let myUrl;
if (!params) {
myUrl = content.replace(/<.*?>/g,"");
} else {
myUrl = params.substr(1);
}
urlPattern.lastIndex = 0;
if ( !urlPattern.test( myUrl ) ) {
myUrl = "#";
}
return '<a href="' + myUrl + '" target="_blank">';
},
closeTag: function(params,content) {
return '</a>';
}
},
*/
}
initialize();
}
}

View File

View File

@ -11,7 +11,9 @@ namespace Modals {
return header;
},
body: function () {
let tag = $("#tmpl_change_volume").renderTag();
let tag = $("#tmpl_change_volume").renderTag({
max_volume: 200
});
tag.find(".volume_slider").on("change",_ => updateCallback(tag.find(".volume_slider").val()));
tag.find(".volume_slider").on("input",_ => updateCallback(tag.find(".volume_slider").val()));
//connect_address
@ -63,4 +65,82 @@ namespace Modals {
connectModal.open();
updateCallback(current * 100);
}
/* Units are between 0 and 1 */
export function spawnChangeRemoteVolume(current: number, max_value: number, callback: (value: number) => void) {
let update_volume: (number) => void;
let current_value = current; /* between 0 and 100! */
const modal = createModal({
header: function() {
let header = $.spawn("div");
header.text(tr("Change volume"));
return header;
},
body: function () {
let tag = $("#tmpl_change_volume").renderTag({
max_volume: Math.ceil(max_value * 100)
});
tag.find(".volume_slider").on("change",_ => update_volume(tag.find(".volume_slider").val()));
tag.find(".volume_slider").on("input",_ => update_volume(tag.find(".volume_slider").val()));
//connect_address
return tag;
},
footer: function () {
let tag = $.spawn("div");
tag.css("text-align", "right");
tag.css("margin-top", "3px");
tag.css("margin-bottom", "6px");
tag.addClass("modal-button-group");
{
let button_apply = $.spawn("button");
button_apply.text(tr("Apply"));
button_apply.on("click", () => {
callback(current_value / 100);
});
tag.append(button_apply);
}
{
let button_reset = $.spawn("button");
button_reset.text(tr("Reset"));
button_reset.on("click", () => update_volume(max_value * 100));
tag.append(button_reset);
}
{
let button_cancel = $.spawn("button");
button_cancel.text(tr("Cancel"));
button_cancel.on("click", () => modal.close());
tag.append(button_cancel);
}
{
let button_ok = $.spawn("button");
button_ok.text(tr("OK"));
button_ok.on("click", () => {
callback(current_value / 100);
modal.close();
});
tag.append(button_ok);
}
return tag;
},
width: 600
});
update_volume = value => {
modal.htmlTag.find(".volume_slider").val(value);
const tag_display = modal.htmlTag.find(".display_volume");
tag_display.html(value + " %");
current_value = value;
};
modal.open();
update_volume(current * 100);
}
}

View File

@ -0,0 +1,356 @@
/// <reference path="../../utils/modal.ts" />
/// <reference path="../../proto.ts" />
/// <reference path="../../client.ts" />
namespace Modals {
export function spawnPlaylistSongInfo(song: PlaylistSong) {
let modal: Modal;
modal = createModal({
header: tr("Song info"),
body: () => {
try {
(<any>song).metadata = JSON.parse(song.song_metadata);
} catch(e) {}
let template = $("#tmpl_playlist_edit-song_info").renderTag(song);
template = $.spawn("div").append(template);
const text_area = template.find(".property-metadata-raw textarea");
template.find(".toggle-metadata").on('click', event => {
if(text_area.is(":visible")) {
template.find(".toggle-metadata").text("show");
} else {
template.find(".toggle-metadata").text("hide");
}
text_area.slideToggle({duration: 250});
});
text_area.hide();
return template;
},
footer: undefined,
width: 750
});
modal.open();
}
export function spawnSongAdd(playlist: Playlist, callback_add: (url: string, loader: string) => any) {
let modal: Modal;
modal = createModal({
header: tr("Add a song"),
body: () => {
let template = $("#tmpl_playlist_edit-song_add").renderTag();
template = $.spawn("div").append(template);
const url = template.find(".property-url .value");
const url_loader = template.find(".property-loader .value");
const button_add = template.find(".container-buttons .button-add");
const button_cancel = template.find(".container-buttons .button-cancel");
url.on('change keyup', event => {
button_add.prop("disabled", url.val().toString().length == 0);
}).trigger('change');
button_cancel.on('click', event => modal.close());
button_add.on('click', event => {
callback_add(url.val() as string, url_loader.val() as string);
modal.close();
});
return template;
},
footer: undefined,
width: 750
});
modal.open();
}
export function spawnPlaylistEdit(client: TSClient, playlist: Playlist) {
let modal: Modal;
let changed_properties = {};
let changed_permissions = {};
let callback_permission_update: () => any;
const update_save = () => {
const save_button = modal.htmlTag.find(".buttons .button-save");
save_button.prop("disabled", (Object.keys(changed_properties).length + Object.keys(changed_permissions).length) == 0);
};
modal = createModal({
header: tr("Edit playlist"),
body: () => {
let template = $("#tmpl_playlist_edit").renderTag().tabify();
template = $.spawn("div").append(template);
callback_permission_update = apply_permissions(template, client, playlist, (key, value) => {
console.log("Change permission %o => %o", key, value);
changed_permissions[key] = value;
update_save();
});
const callback_song_id = apply_songs(template, client, playlist);
apply_properties(template, client, playlist, (key, value) => {
console.log("Change property %o => %o", key, value);
changed_properties[key] = value;
update_save();
}, callback_song_id);
template.find(".buttons .button-save").on('click', event => {
if(Object.keys(changed_properties).length != 0) {
changed_properties["playlist_id"] = playlist.playlist_id;
client.serverConnection.sendCommand("playlistedit", changed_properties).then(() => {
changed_properties = {};
update_save();
}).catch(error => {
if(error instanceof CommandResult)
error = error.extra_message || error.message;
createErrorModal(tr("Failed to change properties."), tr("Failed to change playlist properties.<br>Error: ") + error).open();
});
}
if(Object.keys(changed_permissions).length != 0) {
const array: any[] = [];
for(const permission_key of Object.keys(changed_permissions)) {
array.push({
permvalue: changed_permissions[permission_key],
permnegated: false,
permskip: false,
permsid: permission_key
});
}
array[0]["playlist_id"] = playlist.playlist_id;
client.serverConnection.sendCommand("playlistaddperm", array).then(() => {
changed_permissions = {};
update_save();
}).catch(error => {
if(error instanceof CommandResult)
error = error.extra_message || error.message;
createErrorModal(tr("Failed to change permission."), tr("Failed to change playlist permissions.<br>Error: ") + error).open();
});
}
});
template.find(".buttons .button-close").on('click', event => {
if((Object.keys(changed_properties).length + Object.keys(changed_permissions).length) != 0) {
spawnYesNo(tr("Are you sure?"), tr("Do you really want to discard all your changes?"), result => {
if(result)
modal.close();
});
return;
}
modal.close();
});
return template;
},
footer: undefined,
width: 750
});
update_save();
modal.open();
return modal;
}
function apply_songs(tag: JQuery, client: TSClient, playlist: Playlist) {
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
const song_tag = tag.find(".container-songs");
let replaying_song_id: number = 0;
let selected_song: PlaylistSong;
const set_song_info = (text: string) => {
const tag = song_tag.find(".info-message");
if(text && text.length > 0) {
tag.text(text).show();
} else
tag.hide();
};
const set_current_song = (id: number) => {
/* this method shall enforce an update */
replaying_song_id = id;
update_songs();
};
const update_songs = () => {
set_song_info(tr("loading song list"));
client.serverConnection.helper.request_playlist_songs(playlist.playlist_id).then(result => {
const entries_tag = song_tag.find(".song-list-entries");
const entry_template = $("#tmpl_playlist_edit-song_entry");
entries_tag.empty();
for(const song of result) {
const rendered = entry_template.renderTag(song);
rendered.find(".button-info").on('click', event => {
spawnPlaylistSongInfo(song);
});
const button_delete = rendered.find(".button-delete");
if(!owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_SONG_REMOVE_POWER).granted(playlist.needed_power_song_remove))
button_delete.detach();
else
button_delete.on('click', event => {
client.serverConnection.sendCommand("playlistsongremove", {
playlist_id: playlist.playlist_id,
song_id: song.song_id
}).then(() => {
rendered.slideToggle({duration: 250, done(animation: JQuery.Promise<any>, jumpedToEnd: boolean): void {
rendered.detach();
}});
rendered.hide(250);
}).catch(error => {
if(error instanceof CommandResult)
error = error.extra_message || error.message;
createErrorModal(tr("Failed to remove song."), tr("Failed to remove song/url from the playlist.<br>Error: ") + error).open();
});
});
if(song.song_id == replaying_song_id)
rendered.addClass("playing");
rendered.on('click', event => {
selected_song = song;
entries_tag.find(".selected").removeClass("selected");
rendered.addClass("selected");
});
entries_tag.append(rendered);
}
const entry_container = song_tag.find(".song-list-entries-container");
if(entry_container.hasScrollBar())
entry_container.addClass("scrollbar");
set_song_info("displaying " + result.length + " songs");
}).catch(error => {
console.error(error);
set_song_info(tr("failed to load song list"));
//TODO improve error handling!
});
};
song_tag.find(".button-refresh").on('click', event => update_songs());
song_tag.find(".button-song-add").on('click', event => {
spawnSongAdd(playlist, (url, loader) => {
//playlist_id invoker previous url
client.serverConnection.sendCommand("playlistsongadd", {
playlist_id: playlist.playlist_id,
invoker: loader,
url: url
}).then(() => {
update_songs();
}).catch(error => {
if(error instanceof CommandResult)
error = error.extra_message || error.message;
createErrorModal(tr("Failed to add song."), tr("Failed to add song/url to the playlist.<br>Error: ") + error).open();
});
});
}).prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_SONG_ADD_POWER).granted(playlist.needed_power_song_add));
/* setTimeout(update_songs, 100); */ /* We dont have to call that here because it will get called over set_current_song when we received the current song id */
return set_current_song;
}
function apply_permissions(tag: JQuery, client: TSClient, playlist: Playlist, change_permission: (key: string, value: number) => any) {
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
const permission_tag = tag.find(".container-permissions");
const nopermission_tag = tag.find(".container-no-permissions");
const update_permissions = () => {
if(!client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST).granted(1)) {
nopermission_tag.show();
permission_tag.hide();
} else {
nopermission_tag.hide();
permission_tag.show();
permission_tag.find(".permission input").prop("disabled", true);
client.permissions.requestPlaylistPermissions(playlist.playlist_id).then(permissions => {
permission_tag.find(".permission input")
.val(0)
.prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_PERMISSION_MODIFY_POWER).granted(playlist.needed_power_permission_modify));
for(const permission of permissions) {
const tag = permission_tag.find(".permission[permission='" + permission.type.name + "']");
if(permission.value != -2)
tag.find("input").val(permission.value);
}
});
}
};
permission_tag.find(".permission").each((index, _element) => {
const element = $(_element);
element.find("input").on('change', event => {
console.log(element.find("input").val());
change_permission(element.attr("permission"), parseInt(element.find("input").val().toString()));
});
});
update_permissions();
return update_permissions;
}
function apply_properties(tag: JQuery, client: TSClient, playlist: Playlist, change_property: (key: string, value: string) => any, callback_current_song: (id: number) => any) {
const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id;
client.serverConnection.helper.request_playlist_info(playlist.playlist_id).then(info => {
tag.find(".property-owner .value")
.text(info.playlist_owner_name + " (" + info.playlist_owner_dbid + ")");
tag.find(".property-title input")
.val(info.playlist_title)
.prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify))
.on('change', event => {
change_property("playlist_title", (<HTMLInputElement>event.target).value);
});
tag.find(".property-description textarea")
.val(info.playlist_description)
.prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify))
.on('change', event => {
change_property("playlist_description", (<HTMLInputElement>event.target).value);
});
tag.find(".property-type select")
.val(info.playlist_type.toString())
.prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify))
.on('change', event => {
change_property("playlist_description", (<HTMLSelectElement>event.target).selectedIndex.toString());
});
tag.find(".property-replay-mode select")
.val(info.playlist_replay_mode.toString())
.prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify))
.on('change', event => {
change_property("playlist_replay_mode", (<HTMLSelectElement>event.target).selectedIndex.toString());
});
tag.find(".property-flag-delete-played input")
.prop("checked", info.playlist_flag_delete_played)
.prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify))
.on('change', event => {
change_property("playlist_flag_delete_played", (<HTMLInputElement>event.target).checked ? "1" : "0");
});
tag.find(".property-current-song .value")
.text(info.playlist_current_song_id);
callback_current_song(info.playlist_current_song_id);
tag.find(".property-flag-finished input")
.prop("checked", info.playlist_flag_finished)
.prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify))
.on('change', event => {
change_property("playlist_flag_finished", (<HTMLInputElement>event.target).checked ? "1" : "0");
});
}).catch(error => {
if(error instanceof CommandResult)
error = error.extra_message || error.message;
createErrorModal(tr("Failed to query playlist info"), tr("Failed to query playlist info.<br>Error:") + error).open();
});
}
}

View File

@ -0,0 +1,183 @@
/// <reference path="../../utils/modal.ts" />
/// <reference path="../../proto.ts" />
/// <reference path="../../client.ts" />
namespace Modals {
export function spawnPlaylistManage(client: TSClient) {
let modal: Modal;
let selected_playlist: Playlist;
let available_playlists: Playlist[];
let highlight_own = settings.global("playlist-list-highlight-own", true);
const update_selected = () => {
const buttons = modal.htmlTag.find(".header .buttons");
buttons.find(".button-playlist-edit").prop(
"disabled",
!selected_playlist
);
buttons.find(".button-playlist-delete").prop(
"disabled",
!selected_playlist || !( /* not owner or permission */
client.permissions.neededPermission(PermissionType.I_PLAYLIST_DELETE_POWER).granted(selected_playlist.needed_power_delete) || /* client has permissions */
client.getClient().properties.client_database_id == selected_playlist.playlist_owner_dbid /* client is playlist owner */
)
);
buttons.find(".button-playlist-create").prop(
"disabled",
!client.permissions.neededPermission(PermissionType.B_PLAYLIST_CREATE).granted(1)
);
if(selected_playlist) {
buttons.find(".button-playlist-edit").prop(
"disabled",
false
);
}
};
const update_list = async () => {
const info_tag = modal.htmlTag.find(".footer .info a");
info_tag.text("loading...");
selected_playlist = undefined;
update_selected();
try {
available_playlists = await client.serverConnection.helper.request_playlist_list();
} catch(error) {
info_tag.text("failed to query playlist list.");
//FIXME error handling?
return;
}
const entries_tag = modal.htmlTag.find(".playlist-list-entries");
const entry_template = $("#tmpl_playlist_list-list_entry");
entries_tag.empty();
const owndbid = client.getClient().properties.client_database_id;
for(const query of available_playlists) {
const tag = entry_template.renderTag(query).on('click', event => {
entries_tag.find(".entry.selected").removeClass("selected");
$(event.target).parent(".entry").addClass("selected");
selected_playlist = query;
update_selected();
});
if(highlight_own && query.playlist_owner_dbid == owndbid)
tag.addClass("highlighted");
entries_tag.append(tag);
}
const entry_container = modal.htmlTag.find(".playlist-list-entries-container");
if(entry_container.hasScrollBar())
entry_container.addClass("scrollbar");
info_tag.text("Showing " + available_playlists.length + " entries");
update_selected();
};
modal = createModal({
header: tr("Manage playlists"),
body: () => {
let template = $("#tmpl_playlist_list").renderTag();
template = $.spawn("div").append(template);
/* first open the modal */
setTimeout(() => {
const entry_container = template.find(".playlist-list-entries-container");
if(entry_container.hasScrollBar())
entry_container.addClass("scrollbar");
}, 100);
template.find(".footer .buttons .button-refresh").on('click', update_list);
template.find(".button-playlist-create").on('click', event => {
const notify_handler = json => {
client.serverConnection.commandHandler.unset_handler("notifyplaylistcreated", notify_handler);
update_list().then(() => {
spawnYesNo(tr("Playlist created successful"), tr("The playlist has been successfully created.<br>Should we open the editor?"), result => {
if(result) {
for(const playlist of available_playlists) {
if(playlist.playlist_id == json[0]["playlist_id"]) {
spawnPlaylistEdit(client, playlist).close_listener.push(update_list);
return;
}
}
}
});
});
};
client.serverConnection.commandHandler.set_handler("notifyplaylistcreated", notify_handler);
client.serverConnection.sendCommand("playlistcreate").catch(error => {
client.serverConnection.commandHandler.unset_handler("notifyplaylistcreated", notify_handler);
if(error instanceof CommandResult)
error = error.extra_message || error.message;
createErrorModal(tr("Unable to create playlist"), tr("Failed to create playlist<br>Message: ") + error).open();
});
});
template.find(".button-playlist-edit").on('click', event => {
if(!selected_playlist) return;
spawnPlaylistEdit(client, selected_playlist).close_listener.push(update_list);
});
template.find(".button-playlist-delete").on('click', () => {
if(!selected_playlist) return;
Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this playlist?"), result => {
if(result) {
client.serverConnection.sendCommand("playlistdelete", {playlist_id: selected_playlist.playlist_id}).then(() => {
createInfoModal(tr("Playlist deleted successful"), tr("This playlist has been deleted successfully.")).open();
update_list();
}).catch(error => {
if(error instanceof CommandResult) {
/* TODO extra handling here */
//if(error.id == ErrorID.PLAYLIST_IS_IN_USE) { }
error = error.extra_message || error.message;
}
createErrorModal(tr("Unable to delete playlist"), tr("Failed to delete playlist<br>Message: ") + error).open();
});
}
});
});
template.find(".input-search").on('change keyup', () => {
const text = (template.find(".input-search").val() as string || "").toLowerCase();
if(text.length == 0) {
template.find(".playlist-list-entries .entry").show();
} else {
template.find(".playlist-list-entries .entry").each((_, e) => {
const element = $(e);
if(element.text().toLowerCase().indexOf(text) == -1)
element.hide();
else
element.show();
})
}
});
template.find(".button-highlight-own").on('change', event => {
const flag = (<HTMLInputElement>event.target).checked;
settings.changeGlobal("playlist-list-highlight-own", flag);
if(flag) {
const owndbid = client.getClient().properties.client_database_id;
template.find(".playlist-list-entries .entry").each((index, _element) => {
const element = $(_element);
if(parseInt(element.attr("playlist-owner-dbid")) == owndbid)
element.addClass("highlighted");
})
} else {
template.find(".playlist-list-entries .highlighted").removeClass("highlighted");
}
}).prop("checked", highlight_own);
return template;
},
footer: undefined,
width: 750
});
update_list();
modal.open();
}
}

View File

@ -143,6 +143,18 @@ class ServerEntry {
});
});
}
}, {
type: MenuEntryType.ENTRY,
icon: "client-invite_buddy",
name: tr("Invite buddy"),
callback: () => {
const address = this.channelTree.client.serverConnection._remote_address.host + ":" + this.channelTree.client.serverConnection._remote_address.port;
const parameter = "connect_default=1&connect_address=" + encodeURIComponent(address);
const url = document.location.origin + document.location.pathname + "?" + parameter;
copy_to_clipboard(url);
createInfoModal(tr("Buddy invite URL"), tr("Your buddy invite URL:<br>") + url + tr("<bt>This has been copied to your clipboard.")).open();
}
},
MenuEntry.CLOSE(on_close)
);

View File

@ -352,6 +352,14 @@ class ChannelTree {
return undefined;
}
find_client_by_unique_id?(unique_id: string) : ClientEntry {
for(let index = 0; index < this.clients.length; index++) {
if(this.clients[index].properties.client_unique_identifier == unique_id)
return this.clients[index];
}
return undefined;
}
private static same_selected_type(a, b) {
if(a instanceof ChannelEntry)
return b instanceof ChannelEntry;
@ -602,7 +610,7 @@ class ChannelTree {
return new Promise<ChannelEntry>(resolve => { resolve(channel); })
}).then(channel => {
chat.serverChat().appendMessage(tr("Channel {} successfully created!"), true, channel.createChatTag());
chat.serverChat().appendMessage(tr("Channel {} successfully created!"), true, channel.generate_tag(true));
sound.play(Sound.CHANNEL_CREATED);
});
});

2
vendor/bbcode vendored

@ -1 +1 @@
Subproject commit 86dae7fae51db65d63febf4c3e15b8dc629a5732
Subproject commit 8f2626cf1e24edca8484545841967525177ef3aa