Add permissions editor; fixes for bans

This commit is contained in:
calzoneman 2013-05-22 15:38:16 -04:00
parent 12c1f4acf8
commit 4e364f45a6
8 changed files with 360 additions and 249 deletions

View file

@ -41,14 +41,31 @@ var Channel = function(name) {
this.openqueue = false;
this.poll = false;
this.voteskip = false;
this.permissions = {
oplaylistadd: -1,
oplaylistnext: 1.5,
oplaylistmove: 1.5,
oplaylistdelete: 2,
oplaylistjump: 1.5,
playlistadd: 1.5,
playlistnext: 1.5,
playlistmove: 1.5,
playlistdelete: 2,
playlistjump: 1.5,
addnontemp: 2,
settemp: 2,
playlistgeturl: 1.5,
playlistshuffle: 2,
playlistclear: 2,
pollctl: 1.5,
pollvote: -1,
kick: 1.5,
ban: 2,
motdedit: 3,
filteredit: 3,
drink: 1.5
};
this.opts = {
qopen_allow_anon: false,
qopen_allow_guest: true,
qopen_allow_qnext: false,
qopen_allow_move: false,
qopen_allow_playnext: false,
qopen_allow_delete: false,
qopen_temp: true,
allow_voteskip: true,
voteskip_ratio: 0.5,
pagetitle: this.name,
@ -61,6 +78,7 @@ var Channel = function(name) {
new Filter("monospace", "`([^`]+)`", "g", "<code>$1</code>"),
new Filter("bold", "(.)\\*([^\\*]+)\\*", "g", "$1<strong>$2</strong>"),
new Filter("italic", "(^| )_([^_]+)_", "g", "$1<em>$2</em>"),
new Filter("strikethrough", "~~([^~]+)~~", "g", "<s>$1</s>"),
new Filter("inline spoiler", "\\[spoiler\\](.*)\\[\\/spoiler\\]", "ig", "<span class=\"spoiler\">$1</span>"),
];
this.motd = {
@ -92,6 +110,23 @@ var Channel = function(name) {
}
}
/* REGION Permissions */
Channel.prototype.hasPermission = function(user, key) {
if(key.indexOf("playlist") == 0 && this.openqueue) {
var key2 = "o" + key;
var v = this.permissions[key2];
if(typeof v != "number") {
return false;
}
return user.rank >= v;
}
var v = this.permissions[key];
if(typeof v != "number") {
return false;
}
return user.rank >= v;
}
/* REGION Channel data */
Channel.prototype.loadDump = function() {
fs.readFile("chandump/" + this.name, function(err, data) {
@ -132,6 +167,9 @@ Channel.prototype.loadDump = function() {
for(var key in data.opts) {
this.opts[key] = data.opts[key];
}
for(var key in data.permissions) {
this.permissions[key] = data.permissions[key];
}
this.broadcastOpts();
if(data.filters) {
for(var i = 0; i < data.filters.length; i++) {
@ -185,6 +223,7 @@ Channel.prototype.saveDump = function() {
currentTime: this.media ? this.media.currentTime : 0,
queue: this.queue,
opts: this.opts,
permissions: this.permissions,
filters: filts,
motd: this.motd,
logins: this.logins,
@ -297,7 +336,7 @@ Channel.prototype.cacheMedia = function(media) {
}
Channel.prototype.banName = function(actor, name) {
if(!Rank.hasPermission(actor, "ban")) {
if(!this.hasPermission(actor, "ban")) {
return false;
}
@ -334,7 +373,7 @@ Channel.prototype.banName = function(actor, name) {
}
Channel.prototype.unbanName = function(actor, name) {
if(!Rank.hasPermission(actor, "ban")) {
if(!this.hasPermission(actor, "ban")) {
return false;
}
@ -347,7 +386,7 @@ Channel.prototype.unbanName = function(actor, name) {
}
Channel.prototype.tryIPBan = function(actor, data) {
if(!Rank.hasPermission(actor, "ipban")) {
if(!this.hasPermission(actor, "ban")) {
return false;
}
if(typeof data.id != "string" || data.id.length != 15) {
@ -393,7 +432,7 @@ Channel.prototype.tryIPBan = function(actor, data) {
}
Channel.prototype.banIP = function(actor, receiver) {
if(!Rank.hasPermission(actor, "ipban"))
if(!this.hasPermission(actor, "ban"))
return false;
this.ipbans[receiver.ip] = [receiver.name, actor.name];
@ -414,13 +453,9 @@ Channel.prototype.banIP = function(actor, receiver) {
}
Channel.prototype.unbanIP = function(actor, ip) {
if(!Rank.hasPermission(actor, "ipban"))
if(!this.hasPermission(actor, "ban"))
return false;
if(this.getIPRank(ip) >= actor.rank) {
return false;
}
this.ipbans[ip] = null;
if(!this.registered)
@ -513,7 +548,7 @@ Channel.prototype.userJoin = function(user) {
// If the channel is empty and isn't registered, the first person
// gets ownership of the channel (temporarily)
if(this.users.length == 0 && !this.registered) {
user.rank = (user.rank < Rank.Owner) ? Rank.Owner + 7 : user.rank;
user.rank = (user.rank < Rank.Owner) ? 10 : user.rank;
user.socket.emit("channelNotRegistered");
}
this.users.push(user);
@ -534,6 +569,7 @@ Channel.prototype.userJoin = function(user) {
user.socket.emit("newPoll", this.poll.packUpdate());
}
user.socket.emit("channelOpts", this.opts);
user.socket.emit("setPermissions", this.permissions);
user.socket.emit("updateMotd", this.motd);
user.socket.emit("drinkCount", {count: this.drinks});
@ -609,7 +645,7 @@ Channel.prototype.hideIP = function(ip) {
}
Channel.prototype.sendRankStuff = function(user) {
if(Rank.hasPermission(user, "ipban")) {
if(this.hasPermission(user, "ban")) {
var ents = [];
for(var ip in this.ipbans) {
if(this.ipbans[ip] != null) {
@ -651,16 +687,19 @@ Channel.prototype.sendRankStuff = function(user) {
if(user.rank < Rank.Siteadmin) {
disp = "(Hidden)";
}
var banned = (ip in this.ipbans && this.ipbans[ip] != null);
var range = ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, "$1.$2.$3");
banned = banned || (range in this.ipbans && this.ipbans[range] != null);
ents.push({
ip: disp,
id: this.hideIP(ip),
names: this.logins[ip],
banned: (ip in this.ipbans && this.ipbans[ip] != null)
banned: banned
});
}
user.socket.emit("seenlogins", {entries: ents});
}
if(Rank.hasPermission(user, "chatFilter")) {
if(this.hasPermission(user, "filteredit")) {
var filts = new Array(this.filters.length);
for(var i = 0; i < this.filters.length; i++) {
filts[i] = this.filters[i].pack();
@ -833,7 +872,7 @@ Channel.prototype.broadcastBanlist = function() {
}
}
for(var i = 0; i < this.users.length; i++) {
if(Rank.hasPermission(this.users[i], "ipban")) {
if(this.hasPermission(this.users[i], "ban")) {
if(this.users[i].rank >= Rank.Siteadmin) {
this.users[i].socket.emit("banlist", {entries: adminents});
}
@ -857,7 +896,7 @@ Channel.prototype.broadcastChatFilters = function() {
filts[i] = this.filters[i].pack();
}
for(var i = 0; i < this.users.length; i++) {
if(Rank.hasPermission(this.users[i], "chatFilter")) {
if(this.hasPermission(this.users[i], "filteredit")) {
this.users[i].socket.emit("chatFilters", {filters: filts});
}
}
@ -938,7 +977,7 @@ Channel.prototype.autoTemp = function(media, user) {
if(isLive(media.type)) {
media.temp = true;
}
if(user.rank < Rank.Moderator && this.opts.qopen_temp) {
if(!this.hasPermission(user, "addnontemp")) {
media.temp = true;
}
}
@ -1019,14 +1058,7 @@ Channel.prototype.enqueue = function(data, user) {
}
Channel.prototype.tryQueue = function(user, data) {
var anon = (user.name == "");
var guest = (user.name != "" && !user.loggedIn);
var canqueue = (anon && this.opts.qopen_allow_anon) ||
(guest && this.opts.qopen_allow_guest) ||
(!anon && !guest);
canqueue = canqueue && this.openqueue;
canqueue = canqueue || this.leader == user || Rank.hasPermission(user, "queue");
if(!canqueue) {
if(!this.hasPermission(user, "playlistadd")) {
return;
}
if(data.pos == undefined || data.id == undefined) {
@ -1036,14 +1068,12 @@ Channel.prototype.tryQueue = function(user, data) {
return;
}
if(data.pos == "next" && !Rank.hasPermission(user, "queue") &&
this.leader != user &&
!this.opts.qopen_allow_qnext) {
if(data.pos == "next" && !this.hasPermission(user, "playlistnext")) {
return;
}
if(user.rank < Rank.Moderator
&& this.leader != user
&& this.leader != user
&& user.noflood("queue", 1.5)) {
return;
}
@ -1065,7 +1095,7 @@ Channel.prototype.setTemp = function(idx, temp) {
}
Channel.prototype.trySetTemp = function(user, data) {
if(!Rank.hasPermission(user, "settemp")) {
if(!this.hasPermission(user, "settemp")) {
return;
}
if(typeof data.idx != "number" || typeof data.temp != "boolean") {
@ -1103,14 +1133,11 @@ Channel.prototype.dequeue = function(data) {
}
Channel.prototype.tryDequeue = function(user, data) {
if(!Rank.hasPermission(user, "queue") &&
this.leader != user &&
(!this.openqueue ||
this.openqueue && !this.opts.qopen_allow_delete)) {
if(!this.hasPermission(user, "playlistdelete")) {
return;
}
if(data.pos == undefined) {
if(data.pos === undefined) {
return;
}
@ -1135,10 +1162,7 @@ Channel.prototype.playNext = function() {
}
Channel.prototype.tryPlayNext = function(user) {
if(!Rank.hasPermission(user, "queue") &&
this.leader != user &&
(!this.openqueue ||
this.openqueue && !this.opts.qopen_allow_playnext)) {
if(!this.hasPermission(user, "playlistjump")) {
return;
}
@ -1192,14 +1216,11 @@ Channel.prototype.jumpTo = function(pos) {
}
Channel.prototype.tryJumpTo = function(user, data) {
if(!Rank.hasPermission(user, "queue") &&
this.leader != user &&
(!this.openqueue ||
this.openqueue && !this.opts.qopen_allow_playnext)) {
if(!this.hasPermission(user, "playlistjump")) {
return;
}
if(data.pos == undefined) {
if(data.pos === undefined) {
return;
}
@ -1215,7 +1236,7 @@ Channel.prototype.clearqueue = function() {
}
Channel.prototype.tryClearqueue = function(user) {
if(!Rank.hasPermission(user, "queue")) {
if(!this.hasPermission(user, "playlistclear")) {
return;
}
this.clearqueue();
@ -1240,7 +1261,7 @@ Channel.prototype.shufflequeue = function() {
}
Channel.prototype.tryShufflequeue = function(user) {
if(!Rank.hasPermission(user, "queue")) {
if(!this.hasPermission(user, "playlistshuffle")) {
return;
}
this.shufflequeue();
@ -1305,12 +1326,9 @@ Channel.prototype.move = function(data) {
}
Channel.prototype.tryMove = function(user, data) {
if(!Rank.hasPermission(user, "queue") &&
this.leader != user &&
(!this.openqueue ||
this.openqueue && !this.opts.qopen_allow_move)) {
if(!this.hasPermission(user, "playlistmove")) {
return;
}
}
if(data.src == undefined || data.dest == undefined) {
return;
@ -1322,7 +1340,7 @@ Channel.prototype.tryMove = function(user, data) {
/* REGION Polls */
Channel.prototype.tryOpenPoll = function(user, data) {
if(!Rank.hasPermission(user, "poll") && this.leader != user) {
if(!this.hasPermission(user, "pollctl") && this.leader != user) {
return;
}
@ -1337,7 +1355,7 @@ Channel.prototype.tryOpenPoll = function(user, data) {
}
Channel.prototype.tryClosePoll = function(user) {
if(!Rank.hasPermission(user, "poll") && this.leader != user) {
if(!this.hasPermission(user, "pollctl")) {
return;
}
@ -1348,6 +1366,10 @@ Channel.prototype.tryClosePoll = function(user) {
}
Channel.prototype.tryVote = function(user, data) {
if(!this.hasPermission(user, "pollvote")) {
return;
}
if(data.option == undefined) {
return;
}
@ -1419,7 +1441,7 @@ Channel.prototype.removeFilter = function(name, source) {
}
Channel.prototype.tryChangeFilter = function(user, data) {
if(!Rank.hasPermission(user, "chatFilter")) {
if(!this.hasPermission(user, "filteredit")) {
return;
}
@ -1448,6 +1470,16 @@ Channel.prototype.tryChangeFilter = function(user, data) {
}
}
Channel.prototype.tryUpdatePermissions = function(user, perms) {
if(!Rank.hasPermission(user, "channelperms")) {
return;
}
for(var key in perms) {
this.permissions[key] = perms[key];
}
this.sendAll("setPermissions", this.permissions);
}
Channel.prototype.tryUpdateOptions = function(user, data) {
if(!Rank.hasPermission(user, "channelOpts")) {
return;
@ -1515,7 +1547,7 @@ Channel.prototype.updateMotd = function(motd) {
}
Channel.prototype.tryUpdateMotd = function(user, data) {
if(!Rank.hasPermission(user, "updateMotd")) {
if(!this.hasPermission(user, "motdedit")) {
return;
}
@ -1683,6 +1715,9 @@ Channel.prototype.changeLeader = function(name) {
if(this.leader != null) {
var old = this.leader;
this.leader = null;
if(old.rank == 1.5) {
old.rank = old.oldrank;
}
this.broadcastUserUpdate(old);
}
if(name == "") {
@ -1698,6 +1733,10 @@ Channel.prototype.changeLeader = function(name) {
if(this.users[i].name == name) {
this.logger.log("*** Assigned leader: " + name);
this.leader = this.users[i];
if(this.users[i].rank < 1.5) {
this.users[i].oldrank = this.users[i].rank;
this.users[i].rank = 1.5;
}
this.broadcastUserUpdate(this.leader);
}
}

View file

@ -38,6 +38,9 @@ function handle(chan, user, msg, data) {
else if(msg.indexOf("/ban ") == 0) {
handleBan(chan, user, msg.substring(5).split(" "));
}
else if(msg.indexOf("/ipban ") == 0) {
handleIPBan(chan, user, msg.substring(5).split(" "));
}
else if(msg.indexOf("/unban ") == 0) {
handleUnban(chan, user, msg.substring(7).split(" "));
}
@ -51,7 +54,7 @@ function handle(chan, user, msg, data) {
}
function handleKick(chan, user, args) {
if(Rank.hasPermission(user, "kick") && args.length > 0) {
if(chan.hasPermission(user, "kick") && args.length > 0) {
args[0] = args[0].toLowerCase();
var kickee;
for(var i = 0; i < chan.users.length; i++) {
@ -69,8 +72,8 @@ function handleKick(chan, user, args) {
}
}
function handleBan(chan, user, args) {
if(Rank.hasPermission(user, "ipban") && args.length > 0) {
function handleIPBan(chan, user, args) {
if(chan.hasPermission(user, "ban") && args.length > 0) {
args[0] = args[0].toLowerCase();
var kickee;
for(var i = 0; i < chan.users.length; i++) {
@ -89,15 +92,27 @@ function handleBan(chan, user, args) {
}
}
function handleBan(chan, user, args) {
if(chan.hasPermission(user, "ban") && args.length > 0) {
args[0] = args[0].toLowerCase();
chan.banName(user, args[0]);
}
}
function handleUnban(chan, user, args) {
if(Rank.hasPermission(user, "ipban") && args.length > 0) {
if(chan.hasPermission(user, "ban") && args.length > 0) {
chan.logger.log("*** " + user.name + " unbanned " + args[0]);
chan.unbanIP(user, args[0]);
if(args[0].match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/)) {
chan.unbanIP(user, args[0]);
}
else {
chan.unbanName(user, args[0]);
}
}
}
function handlePoll(chan, user, msg) {
if(Rank.hasPermission(user, "poll") || chan.leader == user) {
if(chan.hasPermission(user, "poll")) {
var args = msg.split(",");
var title = args[0];
args.splice(0, 1);
@ -109,7 +124,7 @@ function handlePoll(chan, user, msg) {
}
function handleDrink(chan, user, msg, data) {
if(!Rank.hasPermission(user, "drink") && chan.leader != user) {
if(!chan.hasPermission(user, "drink")) {
return;
}

View file

@ -9,6 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
exports.Anonymous = -1;
exports.Guest = 0;
exports.Member = 1;
exports.Moderator = 2;
@ -23,6 +24,7 @@ var permissions = {
chatFilter : exports.Owner,
setcss : exports.Owner,
setjs : exports.Owner,
channelperms : exports.Owner,
queue : exports.Moderator,
assignLeader : exports.Moderator,
kick : exports.Moderator,

View file

@ -22,7 +22,7 @@ var User = function(socket, ip) {
this.ip = ip;
this.socket = socket;
this.loggedIn = false;
this.rank = Rank.Guest;
this.rank = Rank.Anonymous;
this.channel = null;
this.name = "";
this.meta = {
@ -346,6 +346,12 @@ User.prototype.initCallbacks = function() {
}
}.bind(this));
this.socket.on("setPermissions", function(data) {
if(this.channel != null) {
this.channel.tryUpdatePermissions(this, data);
}
}.bind(this));
this.socket.on("setChannelCSS", function(data) {
if(this.channel != null) {
this.channel.trySetCSS(this, data);
@ -474,6 +480,7 @@ User.prototype.login = function(name, pw, session) {
}
else {
lastguestlogin[this.ip] = Date.now();
this.rank = Rank.Guest;
Logger.syslog.log(this.ip + " signed in as " + name);
this.name = name;
this.loggedIn = false;

View file

@ -232,7 +232,13 @@ Callbacks = {
$("#voteskip").attr("disabled", false);
else
$("#voteskip").attr("disabled", true);
handleRankChange();
handlePermissionChange();
},
setPermissions: function(perms) {
CHANPERMS = perms;
genPermissionsEditor();
handlePermissionChange();
},
channelCSSJS: function(data) {
@ -428,7 +434,7 @@ Callbacks = {
rank: function(data) {
RANK = data.rank;
handleRankChange();
handlePermissionChange();
},
register: function(data) {
@ -502,7 +508,7 @@ Callbacks = {
PROFILE.image = data.profile.image;
LEADER = data.leader;
RANK = data.rank;
handleRankChange();
handlePermissionChange();
if(LEADER) {
// I'm a leader! Set up sync function
sendVideoUpdate = function() {

View file

@ -14,9 +14,10 @@ var LEADER = false;
var PLAYER = new Media();
var MEDIATYPE = "null";
var POSITION = -1;
var RANK = 0;
var RANK = -1;
var OPENQUEUE = false;
var CHANNELOPTS = {};
var CHANPERMS = {};
var GRABBEDLI = null;
var OLDINDEX = -1;
var CHATHIST = [];
@ -346,13 +347,6 @@ $("#opt_submit").click(function() {
var css = $("#opt_customcss").val();
var ratio = +$("#opt_voteskip_ratio").val() || 0.5;
opts = {
qopen_allow_anon: $("#opt_qopen_allow_anon").prop("checked"),
qopen_allow_guest: $("#opt_qopen_allow_guest").prop("checked"),
qopen_allow_qnext: $("#opt_qopen_allow_qnext").prop("checked"),
qopen_allow_move: $("#opt_qopen_allow_move").prop("checked"),
qopen_allow_delete: $("#opt_qopen_allow_delete").prop("checked"),
qopen_allow_playnext: $("#opt_qopen_allow_playnext").prop("checked"),
qopen_temp: $("#opt_qopen_temp").prop("checked"),
allow_voteskip: $("#opt_allow_voteskip").prop("checked"),
voteskip_ratio: ratio,
pagetitle: ptitle,
@ -380,6 +374,15 @@ $("#show_chancontrols").click(function() {
$("#chancontrols").show();
});
$("#show_chanperms").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_chanperms").parent().addClass("active");
$("#chanperms").show();
});
$("#show_banlist").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
@ -439,17 +442,12 @@ $("#updatejs").click(function() {
});
$("#show_filtereditor").click(function() {
if(RANK >= Rank.Owner) {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_filtereditor").parent().addClass("active");
$("#filtereditor").show();
}
else {
alert("Only channel administrators can change filters");
}
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_filtereditor").parent().addClass("active");
$("#filtereditor").show();
});
$("#show_acl").click(function() {

View file

@ -93,6 +93,17 @@ function addUserDropdown(entry, name) {
}
}.bind(a));
if(hasPermission("kick")) {
var kick = $("<li />").appendTo(ul);
var a = $("<a />").attr("tabindex", "-1").attr("href", "javascript:void(0);").appendTo(kick);
a.text("Kick");
a.click(function() {
socket.emit("chatMsg", {
msg: "/kick " + name
});
});
}
if(RANK >= Rank.Moderator) {
$("<li />").addClass("divider").appendTo(ul);
@ -114,15 +125,6 @@ function addUserDropdown(entry, name) {
});
});
var kick = $("<li />").appendTo(ul);
var a = $("<a />").attr("tabindex", "-1").attr("href", "javascript:void(0);").appendTo(kick);
a.text("Kick");
a.click(function() {
socket.emit("chatMsg", {
msg: "/kick " + name
});
});
var ban = $("<li />").appendTo(ul);
var a = $("<a />").attr("tabindex", "-1").attr("href", "javascript:void(0);").appendTo(ban);
a.text("IP Ban");
@ -295,19 +297,9 @@ function makeQueueEntry(video) {
// Add buttons to a queue list entry
function addQueueButtons(li) {
if(RANK < Rank.Moderator && !LEADER) {
if(!CHANNELOPTS.qopen_allow_delete
&& !CHANNELOPTS.qopen_allow_move
&& !CHANNELOPTS.qopen_allow_qnext) {
return;
}
}
var fullperms = LEADER || RANK >= Rank.Moderator;
var btnstrip = $("<div />").attr("class", "btn-group qe_buttons").prependTo(li);
if(CHANNELOPTS.qopen_allow_move || fullperms) {
if(hasPermission("playlistmove")) {
var btnMove = $("<button />").addClass("btn qe_btn").appendTo(btnstrip);
$("<i />").addClass("icon-resize-vertical").appendTo(btnMove);
// Callback time
@ -326,7 +318,7 @@ function addQueueButtons(li) {
});
}
if(CHANNELOPTS.qopen_allow_delete || fullperms) {
if(hasPermission("playlistdelete")) {
var btnRemove = $("<button />").attr("class", "btn btn-danger qe_btn").appendTo(btnstrip);
$("<i />").attr("class", "icon-remove").appendTo(btnRemove);
$(btnRemove).click(function() {
@ -336,7 +328,7 @@ function addQueueButtons(li) {
});
}
if(CHANNELOPTS.qopen_allow_playnext || fullperms) {
if(hasPermission("playlistjump")) {
var btnPlay = $("<button />").attr("class", "btn btn-success qe_btn").appendTo(btnstrip);
$("<i />").attr("class", "icon-play").appendTo(btnPlay);
$(btnPlay).click(function() {
@ -347,7 +339,7 @@ function addQueueButtons(li) {
});
}
if(CHANNELOPTS.qopen_allow_qnext || fullperms) {
if(hasPermission("playlistnext")) {
var btnNext = $("<button />").attr("class", "btn qe_btn").appendTo(btnstrip);
$("<i/>").addClass("icon-share-alt").appendTo(btnNext);
$(btnNext).click(function() {
@ -360,7 +352,7 @@ function addQueueButtons(li) {
});
}
if(RANK >= Rank.Moderator) {
if(hasPermission("settemp")) {
var btnTemp = $("<button />").attr("class", "btn qe_btn").appendTo(btnstrip);
var temp = $(li).hasClass("alert-error");
$("<i/>").addClass("icon-flag").appendTo(btnTemp);
@ -396,8 +388,7 @@ function addQueueButtons(li) {
function rebuildPlaylist() {
$("#queue li").each(function() {
$(this).find(".btn-group").remove();
if(RANK >= Rank.Moderator || LEADER || canQueue())
addQueueButtons(this);
addQueueButtons(this);
});
}
@ -405,7 +396,7 @@ function rebuildPlaylist() {
function addLibraryButtons(li, id, yt) {
var btnstrip = $("<div />").attr("class", "btn-group qe_buttons").prependTo(li);
if(RANK >= Rank.Moderator || LEADER || (OPENQUEUE && CHANNELOPTS.qopen_allow_qnext)) {
if(hasPermission("playlistnext")) {
var btnNext = $("<button />").addClass("btn qe_btn")
.text("Next")
.appendTo(btnstrip);
@ -427,20 +418,6 @@ function addLibraryButtons(li, id, yt) {
}
var btnEnd = $("<button />").addClass("btn qe_btn").text("End").appendTo(btnstrip);
if(RANK >= Rank.Moderator) {
var btnDelete = $("<button/>").addClass("btn qe_btn btn-danger").appendTo(btnstrip);
$("<i/>").addClass("icon-remove").appendTo(btnDelete);
btnDelete.click(function() {
socket.emit("uncache", {
id: id
});
$(li).hide("blind", function() {
$(li).remove();
});
});
}
btnEnd.click(function() {
if(yt) {
socket.emit("queue", {
@ -457,6 +434,18 @@ function addLibraryButtons(li, id, yt) {
}
});
if(RANK >= Rank.Moderator) {
var btnDelete = $("<button/>").addClass("btn qe_btn btn-danger").appendTo(btnstrip);
$("<i/>").addClass("icon-remove").appendTo(btnDelete);
btnDelete.click(function() {
socket.emit("uncache", {
id: id
});
$(li).hide("blind", function() {
$(li).remove();
});
});
}
}
// Rearranges the queue
@ -618,85 +607,59 @@ function canQueue() {
return canqueue;
}
function handleRankChange() {
rebuildPlaylist();
if(RANK >= 10) {
$("#drop_channel").parent().css("display", "");
function handlePermissionChange() {
function setVisible(selector, bool) {
var disp = bool ? "" : "none";
$(selector).css("display", disp);
}
if(RANK >= Rank.Owner) {
$("#show_jseditor").parent().css("display", "");
$("#show_csseditor").parent().css("display", "");
}
else {
$("#show_jseditor").parent().css("display", "none");
$("#show_csseditor").parent().css("display", "none");
}
if(RANK >= Rank.Moderator) {
$("#qlockbtn").css("display", "block");
var users = $("#userlist").children();
for(var i = 0; i < users.length; i++) {
addUserDropdown(users[i], users[i].children[1].innerHTML);
}
$("#getplaylist").css("width", "34%");
$("#clearplaylist").css("display", "");
$("#shuffleplaylist").css("display", "");
$("#modnav").show();
$("#chancontrols").show();
var val = false;
if(RANK < Rank.Owner) {
val = "disabled";
}
$("#opt_pagetitle").attr("disabled", val);
$("#opt_customcss").attr("disabled", val);
$("#opt_customjs").attr("disabled", val);
$("#opt_show_public").attr("disabled", val);
$("#show_filtereditor").attr("disabled", val);
$("#show_acl").attr("disabled", val);
}
else {
if(!LEADER) {
if(canQueue()) {
$("#playlist_controls").css("display", "");
if(CHANNELOPTS.qopen_allow_qnext)
$("#queue_next").attr("disabled", false);
else
$("#queue_next").attr("disabled", true);
if(CHANNELOPTS.qopen_allow_playnext)
$("#play_next").attr("disabled", false);
else
$("#play_next").attr("disabled", true);
}
else {
$("#playlist_controls").css("display", "none");
}
$("#pollcontainer .active").each(function() {
$(this).find(".btn-danger").remove();
});
if(RANK < 2) {
$(".modonly").hide();
}
setVisible("#playlist_controls", hasPermission("playlistadd"));
$("#queue_next").attr("disabled", !hasPermission("playlistnext"));
setVisible("#qlockbtn", RANK >= 2);
setVisible("#getplaylist", hasPermission("playlistgeturl"));
setVisible("#clearplaylist", hasPermission("playlistclear"));
setVisible("#shuffleplaylist", hasPermission("playlistshuffle"));
setVisible("#modnav", RANK >= 2);
setVisible("#chanperms_tab", RANK >= 3);
setVisible("#banlist_tab", hasPermission("ban"));
setVisible("#motdeditor_tab", hasPermission("motdedit"));
setVisible("#csseditor_tab", RANK >= 3);
setVisible("#jseditor_tab", RANK >= 3);
setVisible("#filtereditor_tab", hasPermission("filteredit"));
setVisible("#acl_tab", RANK >= 3);
setVisible("#dropchannel_tab", RANK >= 10);
$("#pollcontainer .active").find(".btn-danger").remove();
if(hasPermission("pollctl")) {
var poll = $("#pollcontainer .active");
if(poll.length > 0) {
$("<button/>").addClass("btn btn-danger pull-right")
.text("End Poll")
.insertAfter(poll.find(".close"))
.click(function() {
socket.emit("closePoll");
});
}
$("#getplaylist").css("width", "100%");
$("#clearplaylist").css("display", "none");
$("#shuffleplaylist").css("display", "none");
}
if(RANK >= Rank.Moderator || LEADER) {
$("#playlist_controls").css("display", "block");
$("#playlist_controls button").each(function() {
$(this).attr("disabled", false);
});
$("#pollcontainer .active").each(function() {
var btns = $(this).find(".btn-danger");
if(btns.length == 0) {
$("<button/>").addClass("btn btn-danger pull-right")
.text("End Poll")
.insertAfter($(this).find(".close"))
.click(function() {
socket.emit("closePoll")
});
}
});
var poll = $("#pollcontainer .active");
if(poll.length > 0) {
poll.find(".btn").attr("disabled", hasPermission("pollvote"));
}
var users = $("#userlist").children();
for(var i = 0; i < users.length; i++) {
addUserDropdown(users[i], users[i].children[1].innerHTML);
}
rebuildPlaylist();
}
function onWindowFocus() {
clearInterval(TITLE_BLINK);
TITLE_BLINK = false;
@ -1093,3 +1056,112 @@ function idToURL(data) {
}
return entry;
}
function genPermissionsEditor() {
$("#chanperms").html("");
var form = $("<form/>").addClass("form-horizontal")
.attr("action", "javascript:void(0)")
.appendTo($("#chanperms"));
var fs = $("<fieldset/>").appendTo(form);
function makeOption(text, key, permset, defval) {
var group = $("<div/>").addClass("control-group")
.appendTo(fs);
$("<label/>").addClass("control-label")
.text(text)
.appendTo(group);
var controls = $("<div/>").addClass("controls")
.appendTo(group);
var select = $("<select/>").appendTo(controls);
select.data("key", key);
for(var i = 0; i < permset.length; i++) {
$("<option/>").attr("value", permset[i][1])
.text(permset[i][0])
.attr("selected", defval == permset[i][1])
.appendTo(select);
}
}
function addDivider(text) {
$("<hr/>").appendTo(fs);
$("<h3/>").text(text).appendTo(fs);
}
var standard = [
["Anonymous" , "-1"],
["Guest" , "0"],
["Registered" , "1"],
["Leader" , "1.5"],
["Moderator" , "2"],
["Channel Admin", "3"]
];
var modleader = [
["Leader" , "1.5"],
["Moderator" , "2"],
["Channel Admin", "3"]
];
var modplus = [
["Moderator" , "2"],
["Channel Admin", "3"]
];
addDivider("Open playlist permissions");
makeOption("Add to playlist", "oplaylistadd", standard, CHANPERMS.oplaylistadd+"");
makeOption("Add/move to next", "oplaylistnext", standard, CHANPERMS.oplaylistnext+"");
makeOption("Move playlist items", "oplaylistmove", standard, CHANPERMS.oplaylistmove+"");
makeOption("Delete playlist items", "oplaylistdelete", standard, CHANPERMS.oplaylistdelete+"");
makeOption("Jump to video", "oplaylistjump", standard, CHANPERMS.oplaylistjump+"");
addDivider("General playlist permissions");
makeOption("Add to playlist", "playlistadd", standard, CHANPERMS.playlistadd+"");
makeOption("Add/move to next", "playlistnext", standard, CHANPERMS.playlistnext+"");
makeOption("Move playlist items", "playlistmove", standard, CHANPERMS.playlistmove+"");
makeOption("Delete playlist items", "playlistdelete", standard, CHANPERMS.playlistdelete+"");
makeOption("Jump to video", "playlistjump", standard, CHANPERMS.playlistjump+"");
makeOption("Add nontemporary media", "addnontemp", standard, CHANPERMS.addnontemp+"");
makeOption("Temp/untemp playlist item", "settemp", standard, CHANPERMS.settemp+"");
makeOption("Retrieve playlist URLs", "playlistgeturl", standard, CHANPERMS.playlistgeturl+"");
makeOption("Shuffle playlist", "playlistshuffle", standard, CHANPERMS.playlistshuffle+"");
makeOption("Clear playlist", "playlistclear", standard, CHANPERMS.playlistclear+"");
addDivider("Polls");
makeOption("Open/Close poll", "pollctl", modleader, CHANPERMS.pollctl+"");
makeOption("Vote", "pollvote", standard, CHANPERMS.pollvote+"");
addDivider("Moderation");
makeOption("Kick users", "kick", modleader, CHANPERMS.kick+"");
makeOption("Ban users", "ban", modplus, CHANPERMS.ban+"");
makeOption("Edit MOTD", "motdedit", modplus, CHANPERMS.motdedit+"");
makeOption("Edit chat filters", "filteredit", modplus, CHANPERMS.filteredit+"");
addDivider("Misc");
makeOption("Drink calls", "drink", modleader, CHANPERMS.drink+"");
var submit = $("<button/>").addClass("btn btn-primary").appendTo(fs);
submit.text("Save");
submit.click(function() {
var perms = {};
fs.find("select").each(function() {
perms[$(this).data("key")] = parseFloat($(this).val());
});
socket.emit("setPermissions", perms);
});
}
function hasPermission(key) {
if(key.indexOf("playlist") == 0 && OPENQUEUE) {
var key2 = "o" + key;
var v = CHANPERMS[key2];
if(typeof v != "number") {
return false;
}
return RANK >= v;
}
var v = CHANPERMS[key];
if(typeof v != "number") {
return false;
}
return RANK >= v;
}

View file

@ -113,42 +113,43 @@
<div class="clear: both;"></div>
</div>
<button class="btn btn-danger btn-block" id="qlockbtn" style="display:none;">Unlock Queue</button>
<div class="span12 btn-group" style="margin: 0">
<button class="btn" id="getplaylist" style="width: 100%">Get Playlist URLs</button>
<button class="btn" id="clearplaylist" style="width: 33%; display: none;">Clear Playlist</button>
<button class="btn" id="shuffleplaylist" style="width: 33%; display: none;">Shuffle Playlist</button>
</div>
<button class="btn btn-block" id="getplaylist" style="display: none;">Get Playlist URLs</button>
<button class="btn btn-block" id="clearplaylist" style="display: none;">Clear Playlist</button>
<button class="btn btn-block" id="shuffleplaylist" style="display: none;">Shuffle Playlist</button>
</div>
</div>
</div>
<div class="row" style="display: none;" id="modnav">
<div class="span12" id="modtabs">
<ul class="nav nav-tabs">
<li class="active">
<li class="active" id="chancontrols_tab">
<a href="javascript:void(0)" id="show_chancontrols">Channel Controls</a>
</li>
<li>
<li id="chanperms_tab">
<a href="javascript:void(0)" id="show_chanperms">Channel Permissions</a>
</li>
<li id="banlist_tab">
<a href="javascript:void(0)" id="show_banlist">Ban List</a>
</li>
<li>
<li id="loginlog_tab">
<a href="javascript:void(0)" id="show_loginlog">Connection Log</a>
</li>
<li>
<li id="motdeditor_tab">
<a href="javascript:void(0)" id="show_motdeditor">MOTD</a>
</li>
<li style="display: none">
<li style="display: none" id="csseditor_tab">
<a href="javascript:void(0)" id="show_csseditor">CSS Editor</a>
</li>
<li style="display: none">
<li style="display: none" id="jseditor_tab">
<a href="javascript:void(0)" id="show_jseditor">JS Editor</a>
</li>
<li>
<li id="filtereditor_tab">
<a href="javascript:void(0)" id="show_filtereditor">Chat Filters</a>
</li>
<li>
<li id="acl_tab">
<a href="javascript:void(0)" id="show_acl">Channel Ranks</a>
</li>
<li style="display: none">
<li style="display: none" id="dropchannel_tab">
<a href="javascript:void(0)" id="drop_channel">Unregister Channel</a>
</li>
</ul>
@ -158,37 +159,6 @@
<div class="span12">
<form action="javascript:void(0)">
<fieldset>
<div class="span5">
<label>When the queue is open:</label>
<label class="checkbox">
<input type="checkbox" id="opt_qopen_allow_anon">
Allow anonymous users to add videos
</label>
<label class="checkbox">
<input type="checkbox" id="opt_qopen_allow_guest">
Allow guest users to add videos
</label>
<label class="checkbox">
<input type="checkbox" id="opt_qopen_allow_qnext">
Allow anyone to Queue Next
</label>
<label class="checkbox">
<input type="checkbox" id="opt_qopen_allow_move">
Allow anyone to move videos
</label>
<label class="checkbox">
<input type="checkbox" id="opt_qopen_allow_delete">
Allow anyone to delete videos
</label>
<label class="checkbox">
<input type="checkbox" id="opt_qopen_allow_playnext">
Allow anyone to jump to a video
</label>
<label class="checkbox">
<input type="checkbox" id="opt_qopen_temp">
Videos added by guests are temporary
</label>
</div>
<div class="span5">
<label>Page Title
<input type="text" id="opt_pagetitle" placeholder="CyTube" class="pull-right">
@ -229,6 +199,8 @@
</form>
</div>
</div>
<div class="row modonly" id="chanperms" style="display: none;">
</div>
<div class="row modonly" id="banlist" style="display: none;">
<div class="span12">
<table class="table table-striped">