From 61b80ddf72b2114763b7206c6dc5f464162dac71 Mon Sep 17 00:00:00 2001 From: Michael Ziegler Date: Thu, 18 Jun 2009 01:22:12 +0200 Subject: [PATCH] merged mmServer class into Mumble model, changed channel viewer to use divs instead of a table (can be more cleanly generated), cleaned up views and removed a whole lot of bad(tm) code --- htdocs/mumble/style.css | 41 ++-- pyweb/mumble/mmobjects.py | 141 ++++--------- pyweb/mumble/models.py | 218 +++++++++++++++------ pyweb/mumble/templatetags/mumble_extras.py | 24 ++- pyweb/mumble/views.py | 77 ++------ template/mumble/channel.htm | 17 ++ template/mumble/content.htm | 62 ------ template/mumble/mumble.htm | 79 ++++---- template/mumble/player.htm | 25 +++ template/mumble/server.htm | 9 + 10 files changed, 341 insertions(+), 352 deletions(-) create mode 100644 template/mumble/channel.htm delete mode 100644 template/mumble/content.htm create mode 100644 template/mumble/player.htm create mode 100644 template/mumble/server.htm diff --git a/htdocs/mumble/style.css b/htdocs/mumble/style.css index dc0d69d..c763f05 100644 --- a/htdocs/mumble/style.css +++ b/htdocs/mumble/style.css @@ -1,29 +1,26 @@ -td.mumble img { - padding: 0px; - margin: 0px; - } -a.dingen:link, a.dingen:visited, a.dingen:hover, a.dingen:active { +a.mumble:link, a.mumble:visited, a.mumble:hover, a.mumble:active { font-weight: bold; font-size: 8pt; color: #283E53; text-decoration: none; - } - -td.mumble { - height: 20px; - padding: 0px; - margin: 0px; - line-height: 6px; - border: none; - } +} div.mumble { - background-color: #FFFFFF; - color: #283E53; - padding: 10px; - margin-top: 20px; - margin-bottom: 20px; - width: 80%; - min-height: 250px; - } + background-image: url( /static/mumble/linie_v.png ); + background-repeat: repeat-y; + margin-left: 20px; + margin-bottom: 0; + padding-bottom: 0; +} + +div.mumble img { + padding: 0px; + margin: 0px; +} + +span.mumble { + width: 10em; + float:right; +} + diff --git a/pyweb/mumble/mmobjects.py b/pyweb/mumble/mmobjects.py index e73076a..339af8f 100755 --- a/pyweb/mumble/mmobjects.py +++ b/pyweb/mumble/mmobjects.py @@ -17,99 +17,10 @@ import mctl import datetime -from time import time +from time import time +from os.path import join - -# base = ice.stringToProxy( "Meta:tcp -h 127.0.0.1 -p 6502" ); -# srv = Murmur.ServerPrx.checkedCast( base ); -# met = Murmur.MetaPrx.checkedCast( base ); - -class mmServer( object ): - # channels = dict(); - # players = dict(); - # id = int(); - # rootName = str(); - - def __init__( self, model, ctl ): - #self.dbusObj = serverObj; - self.channels = dict(); - self.players = dict(); - self.id = model.srvid; - self.rootName = model.name; - self.model = model; - - links = dict(); - - chanlist = ctl.getChannels(model.srvid); - # sometimes, ICE seems to return the Channel list in a weird order. - # itercount prevents infinite loops. - itercount = 0; - maxiter = len(chanlist) * 3; - while len(chanlist) and itercount < maxiter: - itercount += 1; - #print len(chanlist) - for theChan in chanlist: - # Channels - Fields: 0 = ID, 1 = Name, 2 = Parent-ID, 3 = Links - - if( theChan[2] == -1 ): - # No parent - self.channels[theChan[0]] = mmChannel( theChan ); - elif theChan[2] in self.channels: - # parent already known - self.channels[theChan[0]] = mmChannel( theChan, self.channels[theChan[2]] ); - else: - continue; - - chanlist.remove( theChan ); - - self.channels[theChan[0]].serverId = self.id; - - # process links - if the linked channels are known, link; else save their ids to link later - for linked in theChan[3]: - if linked in self.channels: - self.channels[theChan[0]].linked.append( self.channels[linked] ); - else: - if linked not in links: - links[linked] = list(); - links[linked].append( self.channels[theChan[0]] ); - #print "Saving link: %s <- %s" % ( linked, self.channels[theChan[0]] ); - - # check if earlier round trips saved channel ids to be linked to the current channel - if theChan[0] in links: - for targetChan in links[theChan[0]]: - targetChan.linked.append( self.channels[theChan[0]] ); - - if self.rootName: - self.channels[0].name = self.rootName; - - for thePlayer in ctl.getPlayers(model.srvid): - # in DBus - # Players - Fields: 0 = UserID, 6 = ChannelID - self.players[ thePlayer[0] ] = mmPlayer( self.model, thePlayer, self.channels[ thePlayer[6] ] ); - - - playerCount = property( - lambda self: len( self.players ), - None - ); - - def is_server( self ): - return True; - def is_channel( self ): - return False; - def is_player( self ): - return False; - - def __str__( self ): - return '' % ( self.rootName, self.id ); - - def visit( self, callback, lvl = 0 ): - if not callable( callback ): - raise Exception, "a callback should be callable..."; - - # call callback first on myself, then visit my root chan - callback( self, lvl ); - self.channels[0].visit( callback, lvl + 1 ); +from django.utils.http import urlquote class mmChannel( object ): @@ -121,7 +32,8 @@ class mmChannel( object ): # linked = list(); # linkedIDs = list(); - def __init__( self, channelObj, parentChan = None ): + def __init__( self, server, channelObj, parentChan = None ): + self.server = server; self.players = list(); self.subchans = list(); self.linked = list(); @@ -131,19 +43,18 @@ class mmChannel( object ): self.parent = parentChan; if self.parent is not None: self.parent.subchans.append( self ); - self.serverId = self.parent.serverId; + def parentChannels( self ): - if self.parent is None or self.parent.is_server() or self.parent.chanid == 0: + if self.parent is None or self.parent.is_server or self.parent.chanid == 0: return []; return self.parent.parentChannels() + [self.parent.name]; - def is_server( self ): - return False; - def is_channel( self ): - return True; - def is_player( self ): - return False; + + is_server = False; + is_channel = True; + is_player = False; + playerCount = property( lambda self: len( self.players ) + sum( [ chan.playerCount for chan in self.subchans ] ), @@ -161,6 +72,25 @@ class mmChannel( object ): sc.visit( callback, lvl + 1 ); for pl in self.players: pl.visit( callback, lvl + 1 ); + + + def getURL( self, forUser = None ): + # mumble://username@host:port/parentchans/self.name + userstr = ""; + + if forUser is not None: + userstr = "%s@" % forUser.name; + + # create list of all my parents and myself + chanlist = self.parentChannels() + [self.name]; + # urlencode channel names + chanlist = [ urlquote( chan ) for chan in chanlist ]; + # create a path by joining the channel names + chanpath = join( *chanlist ); + + return "mumble://%s%s:%d/%s" % ( userstr, self.server.addr, self.server.port, chanpath ); + + url = property( getURL, None ); @@ -206,12 +136,9 @@ class mmPlayer( object ): None ); - def is_server( self ): - return False; - def is_channel( self ): - return False; - def is_player( self ): - return True; + is_server = False; + is_channel = False; + is_player = True; # kept for compatibility to mmChannel (useful for traversal funcs) playerCount = property( lambda self: -1, None ); diff --git a/pyweb/mumble/models.py b/pyweb/mumble/models.py index 62fd033..bcd4e52 100755 --- a/pyweb/mumble/models.py +++ b/pyweb/mumble/models.py @@ -14,20 +14,19 @@ * GNU General Public License for more details. """ -from PIL import Image -from struct import pack, unpack -from zlib import compress, decompress +import socket +from PIL import Image +from struct import pack, unpack +from zlib import compress, decompress from django.contrib.auth.models import User -from django.db import models - -from mmobjects import mmServer, mmACL +from django.db import models from django.conf import settings -from mctl import * +from mmobjects import * +from mctl import * -import socket class Mumble( models.Model ): name = models.CharField( 'Server Name', max_length = 200 ); @@ -48,71 +47,89 @@ class Mumble( models.Model ): channel= models.CharField( 'Channel name regex', max_length=200, default=r'[ \-=\w\#\[\]\{\}\(\)\@\|]+' ); defchan= models.IntegerField( 'Default channel', default=0 ); booted = models.BooleanField( 'Boot Server', default = True ); - - def getServerObject( self ): - return mmServer( self, MumbleCtlBase.newInstance( self.dbus ) ); - + + def __init__( self, *args, **kwargs ): + models.Model.__init__( self, *args, **kwargs ); + self._ctl = None; + self._channels = None; + self._rootchan = None; + def __unicode__( self ): return u'Murmur "%s" (%d)' % ( self.name, self.srvid ); + + is_server = True; + is_channel = False; + is_player = False; + + + # Ctl instantiation + def getCtl( self ): + if not self._ctl: + self._ctl = MumbleCtlBase.newInstance( self.dbus ); + return self._ctl; + + ctl = property( getCtl, None ); + + def save( self, dontConfigureMurmur=False ): if dontConfigureMurmur: # skip murmur configuration, e.g. because we're inserting models for existing servers. return models.Model.save( self ); - + # check if this server already exists, if not call newServer and set my srvid first - - ctl = MumbleCtlBase.newInstance( self.dbus ); + if self.id is None: - self.srvid = ctl.newServer(); - - ctl.setConf( self.srvid, 'host', socket.gethostbyname( self.addr ) ); - ctl.setConf( self.srvid, 'registername', self.name ); - ctl.setConf( self.srvid, 'registerurl', self.url ); - ctl.setConf( self.srvid, 'welcometext', self.motd ); - ctl.setConf( self.srvid, 'password', self.passwd ); - ctl.setConf( self.srvid, 'certificate', self.sslcrt ); - ctl.setConf( self.srvid, 'key', self.sslkey ); - ctl.setConf( self.srvid, 'obfuscate', str(self.obfsc).lower() ); - ctl.setConf( self.srvid, 'playername', self.player ); - ctl.setConf( self.srvid, 'channelname', self.channel ); - ctl.setConf( self.srvid, 'defaultchannel', str(self.defchan) ); + self.srvid = self.ctl.newServer(); + + self.ctl.setConf( self.srvid, 'host', socket.gethostbyname( self.addr ) ); + self.ctl.setConf( self.srvid, 'registername', self.name ); + self.ctl.setConf( self.srvid, 'registerurl', self.url ); + self.ctl.setConf( self.srvid, 'welcometext', self.motd ); + self.ctl.setConf( self.srvid, 'password', self.passwd ); + self.ctl.setConf( self.srvid, 'certificate', self.sslcrt ); + self.ctl.setConf( self.srvid, 'key', self.sslkey ); + self.ctl.setConf( self.srvid, 'obfuscate', str(self.obfsc).lower() ); + self.ctl.setConf( self.srvid, 'playername', self.player ); + self.ctl.setConf( self.srvid, 'channelname', self.channel ); + self.ctl.setConf( self.srvid, 'defaultchannel', str(self.defchan) ); if self.port is not None: - ctl.setConf( self.srvid, 'port', str(self.port) ); + self.ctl.setConf( self.srvid, 'port', str(self.port) ); else: - ctl.setConf( self.srvid, 'port', '' ); + self.ctl.setConf( self.srvid, 'port', '' ); if self.users is not None: - ctl.setConf( self.srvid, 'users', str(self.users) ); + self.ctl.setConf( self.srvid, 'users', str(self.users) ); else: - ctl.setConf( self.srvid, 'users', '' ); + self.ctl.setConf( self.srvid, 'users', '' ); if self.bwidth is not None: - ctl.setConf( self.srvid, 'bandwidth', str(self.bwidth) ); + self.ctl.setConf( self.srvid, 'bandwidth', str(self.bwidth) ); else: - ctl.setConf( self.srvid, 'bandwidth', '' ); + self.ctl.setConf( self.srvid, 'bandwidth', '' ); # registerHostname needs to take the port no into account if self.port and self.port != 64738: - ctl.setConf( self.srvid, 'registerhostname', "%s:%d" % ( self.addr, self.port ) ); + self.ctl.setConf( self.srvid, 'registerhostname', "%s:%d" % ( self.addr, self.port ) ); else: - ctl.setConf( self.srvid, 'registerhostname', self.addr ); - + self.ctl.setConf( self.srvid, 'registerhostname', self.addr ); + if self.supw: - ctl.setSuperUserPassword( self.srvid, self.supw ); + self.ctl.setSuperUserPassword( self.srvid, self.supw ); self.supw = ''; - if self.booted != ctl.isBooted( self.srvid ): + if self.booted != self.ctl.isBooted( self.srvid ): if self.booted: - ctl.start( self.srvid ); + self.ctl.start( self.srvid ); else: - ctl.stop( self.srvid ); + self.ctl.stop( self.srvid ); # Now allow django to save the record set return models.Model.save( self ); + def isUserAdmin( self, user ): if user.is_authenticated(): try: @@ -120,15 +137,83 @@ class Mumble( models.Model ): except MumbleUser.DoesNotExist: return False; return False; - + + + # Deletion handler def deleteServer( self ): # Unregister this player in Murmur via ctroller. - #print MumbleCtlBase.newInstance() - MumbleCtlBase.newInstance( self.dbus ).deleteServer(self.srvid) - + self.ctl.deleteServer(self.srvid) + @staticmethod def pre_delete_listener( **kwargs ): kwargs['instance'].deleteServer(); + + + # Channel lists: flat list + def getChannels( self ): + if self._channels is None: + self._channels = {}; + chanlist = self.ctl.getChannels(self.srvid); + links = {}; + + # sometimes, ICE seems to return the Channel list in a weird order. + # itercount prevents infinite loops. + itercount = 0; + maxiter = len(chanlist) * 3; + while len(chanlist) and itercount < maxiter: + itercount += 1; + for theChan in chanlist: + # Channels - Fields: 0 = ID, 1 = Name, 2 = Parent-ID, 3 = Links + if( theChan[2] == -1 ): + # No parent + self._channels[theChan[0]] = mmChannel( self, theChan ); + elif theChan[2] in self.channels: + # parent already known + self._channels[theChan[0]] = mmChannel( self, theChan, self.channels[theChan[2]] ); + else: + continue; + + chanlist.remove( theChan ); + + self._channels[theChan[0]].serverId = self.id; + + # process links - if the linked channels are known, link; else save their ids to link later + for linked in theChan[3]: + if linked in self._channels: + self._channels[theChan[0]].linked.append( self._channels[linked] ); + else: + if linked not in links: + links[linked] = list(); + links[linked].append( self._channels[theChan[0]] ); + + # check if earlier round trips saved channel ids to be linked to the current channel + if theChan[0] in links: + for targetChan in links[theChan[0]]: + targetChan.linked.append( self._channels[theChan[0]] ); + + self._channels[0].name = self.name; + + self.players = {}; + for thePlayer in self.ctl.getPlayers(self.srvid): + # Players - Fields: 0 = UserID, 6 = ChannelID + self.players[ thePlayer[0] ] = mmPlayer( self, thePlayer, self._channels[ thePlayer[6] ] ); + + return self._channels; + + channels = property( getChannels, None ); + rootchan = property( lambda self: self.channels[0], None ); + + def getURL( self, forUser = None ): + # mumble://username@host:port/ + userstr = ""; + if forUser is not None: + userstr = "%s@" % forUser.name; + + return "mumble://%s%s:%d/" % ( userstr, self.addr, self.port ); + + url = property( getURL, None ); + + class MumbleUser( models.Model ): mumbleid = models.IntegerField( 'Mumble player_id', editable = False, default = -1 ); @@ -137,18 +222,26 @@ class MumbleUser( models.Model ): server = models.ForeignKey( Mumble ); owner = models.ForeignKey( User, null=True, blank=True ); isAdmin = models.BooleanField( 'Admin on root channel', default = False ); - + + + is_server = False; + is_channel = False; + is_player = True; + + def __unicode__( self ): return u"Mumble user %s on %s owned by Django user %s" % ( self.name, self.server, self.owner ); + + def save( self, dontConfigureMurmur=False ): if dontConfigureMurmur: # skip murmur configuration, e.g. because we're inserting models for existing players. return models.Model.save( self ); - # Before the record set is saved, update Murmur via ctroller. - ctl = MumbleCtlBase.newInstance( self.server.dbus ); - + # Before the record set is saved, update Murmur via controller. + ctl = self.server.ctl; + if self.id is None: # This is a new user record, so Murmur doesn't know about it yet self.mumbleid = ctl.registerPlayer(self.server.srvid, self.name); @@ -177,9 +270,11 @@ class MumbleUser( models.Model ): return models.Model.save( self ); + # Admin handlers + def getAdmin( self ): # Get ACL of root Channel, get the admin group and see if I'm in it - acl = mmACL( 0, MumbleCtlBase.newInstance( self.server.dbus ).getACL(self.server.srvid, 0) ); + acl = mmACL( 0, self.server.ctl.getACL(self.server.srvid, 0) ); if not hasattr( acl, "admingroup" ): raise ValueError( "The admin group was not found in the ACL's groups list!" ); @@ -187,7 +282,7 @@ class MumbleUser( models.Model ): def setAdmin( self, value ): # Get ACL of root Channel, get the admin group and see if I'm in it - ctl = MumbleCtlBase.newInstance( self.server.dbus ); + ctl = self.server.ctl; acl = mmACL( 0, ctl.getACL(self.server.srvid, 0) ); if not hasattr( acl, "admingroup" ): @@ -202,20 +297,29 @@ class MumbleUser( models.Model ): ctl.setACL(self.server.srvid, acl); return value; + + # Texture handlers + def getTexture( self ): - return MumbleCtlBase.newInstance( self.server.dbus ).getTexture(self.server.srvid, self.mumbleid); + return self.server.ctl.getTexture(self.server.srvid, self.mumbleid); def setTexture( self, infile ): - MumbleCtlBase.newInstance( self.server.dbus ).setTexture(self.server.srvid, self.mumbleid, infile) - + self.server.ctl.setTexture(self.server.srvid, self.mumbleid, infile) + + + # Deletion handler + @staticmethod def pre_delete_listener( **kwargs ): kwargs['instance'].unregister(); def unregister( self ): # Unregister this player in Murmur via dbus. - MumbleCtlBase.newInstance( self.server.dbus ).unregisterPlayer(self.server.srvid, self.mumbleid) - + self.server.ctl.unregisterPlayer(self.server.srvid, self.mumbleid) + + + # "server" field protection + def __setattr__( self, name, value ): if name == 'server': if self.id is not None and self.server != value: @@ -224,6 +328,8 @@ class MumbleUser( models.Model ): models.Model.__setattr__( self, name, value ); + + from django.db.models import signals signals.pre_delete.connect( Mumble.pre_delete_listener, sender=Mumble ); diff --git a/pyweb/mumble/templatetags/mumble_extras.py b/pyweb/mumble/templatetags/mumble_extras.py index 64f8567..d8ca75e 100644 --- a/pyweb/mumble/templatetags/mumble_extras.py +++ b/pyweb/mumble/templatetags/mumble_extras.py @@ -13,7 +13,8 @@ * GNU General Public License for more details. """ -from django import template +from django import template +from django.template.loader import render_to_string register = template.Library(); @@ -35,3 +36,24 @@ def trunc( string, maxlen = 50 ): return string[:(maxlen - 3)] + "..."; register.filter( 'trunc', trunc ); + + +### FILTER: chanview -- renders an mmChannel / mmPlayer object with the correct template. +def chanview( obj, user = None ): + if obj.is_server: + return render_to_string( 'mumble/server.htm', { 'Server': obj, 'MumbleAccount': user } ); + elif obj.is_channel: + return render_to_string( 'mumble/channel.htm', { 'Channel': obj, 'MumbleAccount': user } ); + elif obj.is_player: + return render_to_string( 'mumble/player.htm', { 'Player': obj, 'MumbleAccount': user } ); + +register.filter( 'chanview', chanview ); + + +### FILTER: chanurl -- creates a connection URL and takes the user's login into account +def chanurl( obj, user ): + return obj.getURL( user ); + +register.filter( 'chanurl', chanurl ); + + diff --git a/pyweb/mumble/views.py b/pyweb/mumble/views.py index ac781a6..12c0bb3 100755 --- a/pyweb/mumble/views.py +++ b/pyweb/mumble/views.py @@ -24,13 +24,8 @@ from django.contrib.auth.decorators import login_required from models import Mumble, MumbleUser from forms import * -from mmobjects import mmServer, mmChannel +from mmobjects import * -# Handler class for all Server specific views - -class Storage( object ): - s = list(); - r = None; def mumbles( request ): @@ -51,7 +46,7 @@ def mumbles( request ): def show( request, server ): "Displays the channel list for the given Server ID." - srv, o = createChannelList( server ); + srv = get_object_or_404( Mumble, id=server ); isAdmin = srv.isUserAdmin( request.user ); @@ -109,17 +104,25 @@ def show( request, server ): return HttpResponseRedirect( '/mumble/%d' % int(server) ); else: textureform = MumbleTextureForm(); - + else: regform = None; textureform = None; + # ChannelTable is a somewhat misleading name, as it actually contains channels and players. + channelTable = []; + for id in srv.channels: + if id != 0: + channelTable.append( srv.channels[id] ); + for id in srv.players: + channelTable.append( srv.players[id] ); + + return render_to_response( 'mumble/mumble.htm', { 'DBaseObject': srv, - 'ServerObject': o, - 'ChannelTable': Storage.s, + 'ChannelTable': channelTable, 'CurrentUserIsAdmin': isAdmin, 'AdminForm': adminform, 'RegForm': regform, @@ -147,59 +150,5 @@ def showTexture( request, server ): return HttpResponse( buffer.getvalue(), "image/png" ); raise Http404(); -def showContent( server, user = None ): - "Renders and returns the channel list for the given Server ID." - from django.template import Context, loader - - srv, o = createChannelList( server ); - - mumbleAcc = None; - if user.is_authenticated(): - mmUsers = MumbleUser.objects.filter( owner = user ); - if mmUsers: - mumbleAcc = mmUsers[0]; - - t_content = loader.get_template( 'mumble/content.htm' ); - c_content = Context( { - 'DBaseObject': srv, - 'ServerObject': o, - 'ChannelTable': Storage.s, - 'user': user, - 'mumbleAccount': mumbleAcc, - "CurrentUserIsAdmin": srv.isUserAdmin( request.user ), - 'MumbleActive': True, - } ); - r_content = t_content.render( c_content ); - - return r_content; - - -def createChannelList( server ): - "Renders the channel list." - srv = get_object_or_404( Mumble, id=server ); - - o = srv.getServerObject(); - - Storage.s = list(); - Storage.r = o.channels[0]; - o.channels[0].visit( renderListItem, 0 ); - - return srv, o; - - -def renderListItem( item, level ): - "Stores a line in the channel list." - if item == Storage.r: - return; - - # Filter channels that don't have players in them and are not a subchannel of root - if level > 1 and item.playerCount == 0: - # I used to test if item is an instance of mmChannel here. For some reason, that doesn't work. Dunno why. - return; - - if isinstance( item, mmChannel ): - Storage.s.append( ( level, item, item.parentChannels() ) ); - else: - Storage.s.append( ( level, item ) ); diff --git a/template/mumble/channel.htm b/template/mumble/channel.htm new file mode 100644 index 0000000..381528c --- /dev/null +++ b/template/mumble/channel.htm @@ -0,0 +1,17 @@ +{% load mumble_extras %} +
+ + {% if Channel.linked %} + linked channel + {% else %} + channel + {% endif %} + + {{ Channel.name|trunc:30 }} + + {% for sub in Channel.subchans %} + {{ sub|chanview:MumbleAccount }} + {% endfor %} + {% for player in Channel.players %}{{ player|chanview }}{% endfor %} +
+ diff --git a/template/mumble/content.htm b/template/mumble/content.htm deleted file mode 100644 index 27c9dde..0000000 --- a/template/mumble/content.htm +++ /dev/null @@ -1,62 +0,0 @@ -{% load mumble_extras %} -
- - - - {% for item in ChannelTable %} - - - - - {% endfor %} -
- mumble - - {{ ServerObject.rootName }} - -
- - {% spaceless %} - {% for num in item.0|mrange %} - | - {% endfor %} - +- - {% endspaceless %} - {% if item.1.is_player %} - player - {{ item.1.name|trunc:30 }} - {% else %} - {% if item.1.linked %} - linked channel - {% else %} - channel - {% endif %} - {{ item.1.name|trunc:30 }} - {% endif %} - - - {% if item.1.userid %} - {% if item.1.isAuthed %} - authed - {% endif %} - {% if item.1.muted %} - muted - {% endif %} - {% if item.1.deafened %} - deafened - {% endif %} - {% if item.1.selfmuted %} - self-muted - {% endif %} - {% if item.1.selfdeafened %} - self-deafened - {% endif %} - {% endif %} -
-
diff --git a/template/mumble/mumble.htm b/template/mumble/mumble.htm index 3beba4c..cd16c5c 100644 --- a/template/mumble/mumble.htm +++ b/template/mumble/mumble.htm @@ -1,12 +1,10 @@ {% extends "index.htm" %} -{% block headtags %} - -{% endblock %} +{% load mumble_extras %} {% block Headline %} - {{ ServerObject.rootName }} + {{ DBaseObject.name }} {% endblock %} {% block LeftColumn %} - {% include "mumble/content.htm" %} + {{ DBaseObject|chanview:MumbleAccount }} {% endblock %} {% block Content %}