mirror of
https://github.com/nikdoof/test-auth.git
synced 2025-12-14 14:52:15 +00:00
Bring piston-api up to current master
Conflicts: .gitignore urls.py
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,6 +4,6 @@
|
|||||||
django/
|
django/
|
||||||
registration/
|
registration/
|
||||||
dbsettings.py
|
dbsettings.py
|
||||||
django_cron
|
|
||||||
django_evolution
|
|
||||||
piston
|
piston
|
||||||
|
django_evolution
|
||||||
|
logs/
|
||||||
|
|||||||
7
cronjobs.txt
Normal file
7
cronjobs.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
ROOT=/home/matalok/auth/auth
|
||||||
|
|
||||||
|
@daily $ROOT/run-cron.py reddit.cron UpdateAPIs > $ROOT/logs/redditapi-update.log 2>&1
|
||||||
|
*/10 * * * * $ROOT/run-cron.py reddit.cron ProcessValidations > $ROOT/logs/reddit-validations.log 2>&1
|
||||||
|
*/5 * * * * $ROOT/run-cron.py eve_api.cron UpdateAPIs > $ROOT/logs/eveapi-update.log 2>&1
|
||||||
|
@hourly $ROOT/run-cron.py sso.cron RemoveInvalidUsers > $ROOT/logs/auth-update.log 2>&1
|
||||||
|
@daily $ROOT/run-cron.py registration.cron RemoveExpiredProfiles > /dev/null 2>&1
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 34
|
|
||||||
/svn/!svn/ver/32/trunk/django_cron
|
|
||||||
END
|
|
||||||
base.py
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 42
|
|
||||||
/svn/!svn/ver/32/trunk/django_cron/base.py
|
|
||||||
END
|
|
||||||
__init__.py
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 46
|
|
||||||
/svn/!svn/ver/16/trunk/django_cron/__init__.py
|
|
||||||
END
|
|
||||||
signals.py
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 44
|
|
||||||
/svn/!svn/ver/8/trunk/django_cron/signals.py
|
|
||||||
END
|
|
||||||
models.py
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 44
|
|
||||||
/svn/!svn/ver/16/trunk/django_cron/models.py
|
|
||||||
END
|
|
||||||
README.txt
|
|
||||||
K 25
|
|
||||||
svn:wc:ra_dav:version-url
|
|
||||||
V 45
|
|
||||||
/svn/!svn/ver/16/trunk/django_cron/README.txt
|
|
||||||
END
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
9
|
|
||||||
|
|
||||||
dir
|
|
||||||
32
|
|
||||||
http://django-cron.googlecode.com/svn/trunk/django_cron
|
|
||||||
http://django-cron.googlecode.com/svn
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2009-10-28T14:21:55.477932Z
|
|
||||||
32
|
|
||||||
jim.mixtake@gmail.com
|
|
||||||
|
|
||||||
|
|
||||||
svn:special svn:externals svn:needs-lock
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
857153d3-df44-0410-aaef-ab1f86cdcd94
|
|
||||||
|
|
||||||
base.py
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
afab29654c6380b9c2ecd00fae92522c
|
|
||||||
2009-10-28T14:21:55.477932Z
|
|
||||||
32
|
|
||||||
jim.mixtake@gmail.com
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
4064
|
|
||||||
|
|
||||||
__init__.py
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
8492cf50017e7eff784fdfbe0cc8b3ba
|
|
||||||
2009-06-02T13:59:46.682354Z
|
|
||||||
16
|
|
||||||
Jim.mixtake
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2566
|
|
||||||
|
|
||||||
signals.py
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
8e3eba63f720bd6d830fc89d7d6be693
|
|
||||||
2008-10-26T12:02:48.705921Z
|
|
||||||
5
|
|
||||||
digitalxero
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1192
|
|
||||||
|
|
||||||
models.py
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
329f05f78f78465d4acfba95c42d32f7
|
|
||||||
2009-06-02T13:59:46.682354Z
|
|
||||||
16
|
|
||||||
Jim.mixtake
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1632
|
|
||||||
|
|
||||||
README.txt
|
|
||||||
file
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2010-02-20T20:20:49.000000Z
|
|
||||||
801836776103697e20d5d9a9446cb28b
|
|
||||||
2009-06-02T13:59:46.682354Z
|
|
||||||
16
|
|
||||||
Jim.mixtake
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1953
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
9
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
= How to install djang-cron =
|
|
||||||
|
|
||||||
1. Put 'django_cron' into your python path
|
|
||||||
|
|
||||||
2. Add 'django_cron' to INSTALLED_APPS in your settings.py file
|
|
||||||
|
|
||||||
3. Add the following code to the beginning of your urls.py file (just after the imports):
|
|
||||||
|
|
||||||
import django_cron
|
|
||||||
django_cron.autodiscover()
|
|
||||||
|
|
||||||
|
|
||||||
4. Create a file called 'cron.py' inside each installed app that you want to add a recurring job to. The app must be installed via the INSTALLED_APPS in your settings.py or the autodiscover will not find it.
|
|
||||||
|
|
||||||
=== Important note ===
|
|
||||||
|
|
||||||
If you are using mod_python, you need to make sure your server is set up to server more than one request per instance, Otherwise it will kill django-cron before the tasks get started. The specific line to look for is in your 'httpd.conf' file:
|
|
||||||
|
|
||||||
|
|
||||||
# THIS IS BAD!!! IT WILL CRIPPLE DJANGO-CRON
|
|
||||||
MaxRequestsPerChild 1
|
|
||||||
|
|
||||||
|
|
||||||
Change it to a value that is large enough that your cron jobs will get run at least once per instance. We're working on resolving this issue without dictating your server config.
|
|
||||||
|
|
||||||
In the meantime, django_cron is best used to execute tasks that occur relatively often (at least once an hour). Try setting MaxRequestsPerChild to 50, 100, or 200
|
|
||||||
|
|
||||||
# Depending on traffic, and your server config, a number between 50 and 500 is probably good
|
|
||||||
# Note: the higher this number, the more memory django is likely to use. Be careful on shared hosting
|
|
||||||
MaxRequestsPerChild 100
|
|
||||||
|
|
||||||
|
|
||||||
== Example cron.py ==
|
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
|
|
||||||
# This is a function I wrote to check a feedback email address
|
|
||||||
# and add it to our database. Replace with your own imports
|
|
||||||
from MyMailFunctions import check_feedback_mailbox
|
|
||||||
|
|
||||||
class CheckMail(Job):
|
|
||||||
"""
|
|
||||||
Cron Job that checks the lgr users mailbox and adds any
|
|
||||||
approved senders' attachments to the db
|
|
||||||
"""
|
|
||||||
|
|
||||||
# run every 300 seconds (5 minutes)
|
|
||||||
run_every = 300
|
|
||||||
|
|
||||||
def job(self):
|
|
||||||
# This will be executed every 5 minutes
|
|
||||||
check_feedback_mailbox()
|
|
||||||
|
|
||||||
cronScheduler.register(CheckMail)
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
from base import Job, cronScheduler
|
|
||||||
|
|
||||||
def autodiscover():
|
|
||||||
"""
|
|
||||||
Auto-discover INSTALLED_APPS cron.py modules and fail silently when
|
|
||||||
not present. This forces an import on them to register any cron jobs they
|
|
||||||
may want.
|
|
||||||
"""
|
|
||||||
import imp
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
for app in settings.INSTALLED_APPS:
|
|
||||||
# For each app, we need to look for an cron.py inside that app's
|
|
||||||
# package. We can't use os.path here -- recall that modules may be
|
|
||||||
# imported different ways (think zip files) -- so we need to get
|
|
||||||
# the app's __path__ and look for cron.py on that path.
|
|
||||||
|
|
||||||
# Step 1: find out the app's __path__ Import errors here will (and
|
|
||||||
# should) bubble up, but a missing __path__ (which is legal, but weird)
|
|
||||||
# fails silently -- apps that do weird things with __path__ might
|
|
||||||
# need to roll their own cron registration.
|
|
||||||
try:
|
|
||||||
app_path = __import__(app, {}, {}, [app.split('.')[-1]]).__path__
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Step 2: use imp.find_module to find the app's admin.py. For some
|
|
||||||
# reason imp.find_module raises ImportError if the app can't be found
|
|
||||||
# but doesn't actually try to import the module. So skip this app if
|
|
||||||
# its admin.py doesn't exist
|
|
||||||
try:
|
|
||||||
imp.find_module('cron', app_path)
|
|
||||||
except ImportError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Step 3: import the app's cron file. If this has errors we want them
|
|
||||||
# to bubble up.
|
|
||||||
__import__("%s.cron" % app)
|
|
||||||
|
|
||||||
# Step 4: once we find all the cron jobs, start the cronScheduler
|
|
||||||
cronScheduler.execute()
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
import cPickle
|
|
||||||
from threading import Timer
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from django.dispatch import dispatcher
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from signals import cron_done
|
|
||||||
import models
|
|
||||||
|
|
||||||
# how often to check if jobs are ready to be run (in seconds)
|
|
||||||
# in reality if you have a multithreaded server, it may get checked
|
|
||||||
# more often that this number suggests, so keep an eye on it...
|
|
||||||
# default value: 300 seconds == 5 min
|
|
||||||
polling_frequency = getattr(settings, "CRON_POLLING_FREQUENCY", 300)
|
|
||||||
|
|
||||||
class Job(object):
|
|
||||||
# 86400 seconds == 24 hours
|
|
||||||
run_every = 86400
|
|
||||||
|
|
||||||
def run(self, *args, **kwargs):
|
|
||||||
self.job()
|
|
||||||
cron_done.send(sender=self, *args, **kwargs)
|
|
||||||
|
|
||||||
def job(self):
|
|
||||||
"""
|
|
||||||
Should be overridden (this way is cleaner, but the old way - overriding run() - will still work)
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CronScheduler(object):
|
|
||||||
def register(self, job_class, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Register the given Job with the scheduler class
|
|
||||||
"""
|
|
||||||
|
|
||||||
job_instance = job_class()
|
|
||||||
|
|
||||||
if not isinstance(job_instance, Job):
|
|
||||||
raise TypeError("You can only register a Job not a %r" % job_class)
|
|
||||||
|
|
||||||
job, created = models.Job.objects.get_or_create(name=str(job_instance.__class__))
|
|
||||||
if created:
|
|
||||||
job.instance = cPickle.dumps(job_instance)
|
|
||||||
job.args = cPickle.dumps(args)
|
|
||||||
job.kwargs = cPickle.dumps(kwargs)
|
|
||||||
job.run_frequency = job_instance.run_every
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
"""
|
|
||||||
Queue all Jobs for execution
|
|
||||||
"""
|
|
||||||
status, created = models.Cron.objects.get_or_create(pk=1)
|
|
||||||
|
|
||||||
# This is important for 2 reasons:
|
|
||||||
# 1. It keeps us for running more than one instance of the
|
|
||||||
# same job at a time
|
|
||||||
# 2. It reduces the number of polling threads because they
|
|
||||||
# get killed off if they happen to check while another
|
|
||||||
# one is already executing a job (only occurs with
|
|
||||||
# multi-threaded servers)
|
|
||||||
if status.executing:
|
|
||||||
return
|
|
||||||
|
|
||||||
status.executing = True
|
|
||||||
try:
|
|
||||||
status.save()
|
|
||||||
except:
|
|
||||||
# this will fail if you're debugging, so we want it
|
|
||||||
# to fail silently and start the timer again so we
|
|
||||||
# can pick up where we left off once debugging is done
|
|
||||||
Timer(polling_frequency, self.execute).start()
|
|
||||||
return
|
|
||||||
|
|
||||||
jobs = models.Job.objects.all()
|
|
||||||
for job in jobs:
|
|
||||||
if job.queued:
|
|
||||||
time_delta = datetime.now() - job.last_run
|
|
||||||
if (time_delta.seconds + 86400*time_delta.days) > job.run_frequency:
|
|
||||||
inst = cPickle.loads(str(job.instance))
|
|
||||||
args = cPickle.loads(str(job.args))
|
|
||||||
kwargs = cPickle.loads(str(job.kwargs))
|
|
||||||
|
|
||||||
try:
|
|
||||||
inst.run(*args, **kwargs)
|
|
||||||
job.last_run = datetime.now()
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
# if the job throws an error, just remove it from
|
|
||||||
# the queue. That way we can find/fix the error and
|
|
||||||
# requeue the job manually
|
|
||||||
job.queued = False
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
status.executing = False
|
|
||||||
status.save()
|
|
||||||
|
|
||||||
# Set up for this function to run again
|
|
||||||
Timer(polling_frequency, self.execute).start()
|
|
||||||
|
|
||||||
|
|
||||||
cronScheduler = CronScheduler()
|
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
from django.db import models
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
class Job(models.Model):
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
|
|
||||||
# time between job runs (in seconds) // default: 1 day
|
|
||||||
run_frequency = models.PositiveIntegerField(default=86400)
|
|
||||||
last_run = models.DateTimeField(default=datetime.now())
|
|
||||||
|
|
||||||
instance = models.TextField()
|
|
||||||
args = models.TextField()
|
|
||||||
kwargs = models.TextField()
|
|
||||||
queued = models.BooleanField(default=True)
|
|
||||||
|
|
||||||
class Cron(models.Model):
|
|
||||||
executing = models.BooleanField(default=False)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
from django.dispatch import Signal
|
|
||||||
|
|
||||||
cron_queued = Signal()
|
|
||||||
cron_done = Signal(providing_args=["job"])
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
= How to install djang-cron =
|
|
||||||
|
|
||||||
1. Put 'django_cron' into your python path
|
|
||||||
|
|
||||||
2. Add 'django_cron' to INSTALLED_APPS in your settings.py file
|
|
||||||
|
|
||||||
3. Add the following code to the beginning of your urls.py file (just after the imports):
|
|
||||||
|
|
||||||
import django_cron
|
|
||||||
django_cron.autodiscover()
|
|
||||||
|
|
||||||
|
|
||||||
4. Create a file called 'cron.py' inside each installed app that you want to add a recurring job to. The app must be installed via the INSTALLED_APPS in your settings.py or the autodiscover will not find it.
|
|
||||||
|
|
||||||
=== Important note ===
|
|
||||||
|
|
||||||
If you are using mod_python, you need to make sure your server is set up to server more than one request per instance, Otherwise it will kill django-cron before the tasks get started. The specific line to look for is in your 'httpd.conf' file:
|
|
||||||
|
|
||||||
|
|
||||||
# THIS IS BAD!!! IT WILL CRIPPLE DJANGO-CRON
|
|
||||||
MaxRequestsPerChild 1
|
|
||||||
|
|
||||||
|
|
||||||
Change it to a value that is large enough that your cron jobs will get run at least once per instance. We're working on resolving this issue without dictating your server config.
|
|
||||||
|
|
||||||
In the meantime, django_cron is best used to execute tasks that occur relatively often (at least once an hour). Try setting MaxRequestsPerChild to 50, 100, or 200
|
|
||||||
|
|
||||||
# Depending on traffic, and your server config, a number between 50 and 500 is probably good
|
|
||||||
# Note: the higher this number, the more memory django is likely to use. Be careful on shared hosting
|
|
||||||
MaxRequestsPerChild 100
|
|
||||||
|
|
||||||
|
|
||||||
== Example cron.py ==
|
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
|
|
||||||
# This is a function I wrote to check a feedback email address
|
|
||||||
# and add it to our database. Replace with your own imports
|
|
||||||
from MyMailFunctions import check_feedback_mailbox
|
|
||||||
|
|
||||||
class CheckMail(Job):
|
|
||||||
"""
|
|
||||||
Cron Job that checks the lgr users mailbox and adds any
|
|
||||||
approved senders' attachments to the db
|
|
||||||
"""
|
|
||||||
|
|
||||||
# run every 300 seconds (5 minutes)
|
|
||||||
run_every = 300
|
|
||||||
|
|
||||||
def job(self):
|
|
||||||
# This will be executed every 5 minutes
|
|
||||||
check_feedback_mailbox()
|
|
||||||
|
|
||||||
cronScheduler.register(CheckMail)
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
from base import Job, cronScheduler
|
|
||||||
|
|
||||||
def autodiscover():
|
|
||||||
"""
|
|
||||||
Auto-discover INSTALLED_APPS cron.py modules and fail silently when
|
|
||||||
not present. This forces an import on them to register any cron jobs they
|
|
||||||
may want.
|
|
||||||
"""
|
|
||||||
import imp
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
for app in settings.INSTALLED_APPS:
|
|
||||||
# For each app, we need to look for an cron.py inside that app's
|
|
||||||
# package. We can't use os.path here -- recall that modules may be
|
|
||||||
# imported different ways (think zip files) -- so we need to get
|
|
||||||
# the app's __path__ and look for cron.py on that path.
|
|
||||||
|
|
||||||
# Step 1: find out the app's __path__ Import errors here will (and
|
|
||||||
# should) bubble up, but a missing __path__ (which is legal, but weird)
|
|
||||||
# fails silently -- apps that do weird things with __path__ might
|
|
||||||
# need to roll their own cron registration.
|
|
||||||
try:
|
|
||||||
app_path = __import__(app, {}, {}, [app.split('.')[-1]]).__path__
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Step 2: use imp.find_module to find the app's admin.py. For some
|
|
||||||
# reason imp.find_module raises ImportError if the app can't be found
|
|
||||||
# but doesn't actually try to import the module. So skip this app if
|
|
||||||
# its admin.py doesn't exist
|
|
||||||
try:
|
|
||||||
imp.find_module('cron', app_path)
|
|
||||||
except ImportError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Step 3: import the app's cron file. If this has errors we want them
|
|
||||||
# to bubble up.
|
|
||||||
__import__("%s.cron" % app)
|
|
||||||
|
|
||||||
# Step 4: once we find all the cron jobs, start the cronScheduler
|
|
||||||
cronScheduler.execute()
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
import cPickle
|
|
||||||
from threading import Timer
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from django.dispatch import dispatcher
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from signals import cron_done
|
|
||||||
import models
|
|
||||||
|
|
||||||
# how often to check if jobs are ready to be run (in seconds)
|
|
||||||
# in reality if you have a multithreaded server, it may get checked
|
|
||||||
# more often that this number suggests, so keep an eye on it...
|
|
||||||
# default value: 300 seconds == 5 min
|
|
||||||
polling_frequency = getattr(settings, "CRON_POLLING_FREQUENCY", 300)
|
|
||||||
|
|
||||||
class Job(object):
|
|
||||||
# 86400 seconds == 24 hours
|
|
||||||
run_every = 86400
|
|
||||||
|
|
||||||
def run(self, *args, **kwargs):
|
|
||||||
self.job()
|
|
||||||
cron_done.send(sender=self, *args, **kwargs)
|
|
||||||
|
|
||||||
def job(self):
|
|
||||||
"""
|
|
||||||
Should be overridden (this way is cleaner, but the old way - overriding run() - will still work)
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CronScheduler(object):
|
|
||||||
def register(self, job_class, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Register the given Job with the scheduler class
|
|
||||||
"""
|
|
||||||
|
|
||||||
job_instance = job_class()
|
|
||||||
|
|
||||||
if not isinstance(job_instance, Job):
|
|
||||||
raise TypeError("You can only register a Job not a %r" % job_class)
|
|
||||||
|
|
||||||
job, created = models.Job.objects.get_or_create(name=str(job_instance.__class__))
|
|
||||||
if created:
|
|
||||||
job.instance = cPickle.dumps(job_instance)
|
|
||||||
job.args = cPickle.dumps(args)
|
|
||||||
job.kwargs = cPickle.dumps(kwargs)
|
|
||||||
job.run_frequency = job_instance.run_every
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
"""
|
|
||||||
Queue all Jobs for execution
|
|
||||||
"""
|
|
||||||
status, created = models.Cron.objects.get_or_create(pk=1)
|
|
||||||
|
|
||||||
# This is important for 2 reasons:
|
|
||||||
# 1. It keeps us for running more than one instance of the
|
|
||||||
# same job at a time
|
|
||||||
# 2. It reduces the number of polling threads because they
|
|
||||||
# get killed off if they happen to check while another
|
|
||||||
# one is already executing a job (only occurs with
|
|
||||||
# multi-threaded servers)
|
|
||||||
if status.executing:
|
|
||||||
return
|
|
||||||
|
|
||||||
status.executing = True
|
|
||||||
try:
|
|
||||||
status.save()
|
|
||||||
except:
|
|
||||||
# this will fail if you're debugging, so we want it
|
|
||||||
# to fail silently and start the timer again so we
|
|
||||||
# can pick up where we left off once debugging is done
|
|
||||||
Timer(polling_frequency, self.execute).start()
|
|
||||||
return
|
|
||||||
|
|
||||||
jobs = models.Job.objects.all()
|
|
||||||
for job in jobs:
|
|
||||||
if job.queued:
|
|
||||||
time_delta = datetime.now() - job.last_run
|
|
||||||
if (time_delta.seconds + 86400*time_delta.days) > job.run_frequency:
|
|
||||||
inst = cPickle.loads(str(job.instance))
|
|
||||||
args = cPickle.loads(str(job.args))
|
|
||||||
kwargs = cPickle.loads(str(job.kwargs))
|
|
||||||
|
|
||||||
try:
|
|
||||||
inst.run(*args, **kwargs)
|
|
||||||
job.last_run = datetime.now()
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
# if the job throws an error, just remove it from
|
|
||||||
# the queue. That way we can find/fix the error and
|
|
||||||
# requeue the job manually
|
|
||||||
job.queued = False
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
status.executing = False
|
|
||||||
status.save()
|
|
||||||
|
|
||||||
# Set up for this function to run again
|
|
||||||
Timer(polling_frequency, self.execute).start()
|
|
||||||
|
|
||||||
|
|
||||||
cronScheduler = CronScheduler()
|
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
from django.db import models
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
class Job(models.Model):
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
|
|
||||||
# time between job runs (in seconds) // default: 1 day
|
|
||||||
run_frequency = models.PositiveIntegerField(default=86400)
|
|
||||||
last_run = models.DateTimeField(default=datetime.now())
|
|
||||||
|
|
||||||
instance = models.TextField()
|
|
||||||
args = models.TextField()
|
|
||||||
kwargs = models.TextField()
|
|
||||||
queued = models.BooleanField(default=True)
|
|
||||||
|
|
||||||
class Cron(models.Model):
|
|
||||||
executing = models.BooleanField(default=False)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
"""
|
|
||||||
Copyright (c) 2007-2008, Dj Gilcrease
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
from django.dispatch import Signal
|
|
||||||
|
|
||||||
cron_queued = Signal()
|
|
||||||
cron_done = Signal(providing_args=["job"])
|
|
||||||
@@ -4,9 +4,20 @@ Admin interface models. Automatically detected by admin.autodiscover().
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from eve_api.models import *
|
from eve_api.models import *
|
||||||
|
|
||||||
|
from eve_api.api_puller.accounts import import_eve_account
|
||||||
|
|
||||||
|
def account_api_update(modeladmin, request, queryset):
|
||||||
|
for obj in queryset:
|
||||||
|
import_eve_account(obj.api_key, obj.api_user_id)
|
||||||
|
|
||||||
|
account_api_update.short_description = "Update account from the EVE API"
|
||||||
|
|
||||||
class EVEAccountAdmin(admin.ModelAdmin):
|
class EVEAccountAdmin(admin.ModelAdmin):
|
||||||
list_display = ('id', 'user', 'api_status', 'api_last_updated')
|
list_display = ('id', 'user', 'api_status', 'api_last_updated')
|
||||||
search_fields = ['id']
|
search_fields = ['id']
|
||||||
|
|
||||||
|
actions = [account_api_update]
|
||||||
|
|
||||||
admin.site.register(EVEAccount, EVEAccountAdmin)
|
admin.site.register(EVEAccount, EVEAccountAdmin)
|
||||||
|
|
||||||
class EVEPlayerCharacterAdmin(admin.ModelAdmin):
|
class EVEPlayerCharacterAdmin(admin.ModelAdmin):
|
||||||
|
|||||||
@@ -22,22 +22,44 @@ def import_eve_account(api_key, user_id):
|
|||||||
"""
|
"""
|
||||||
Imports an account from the API into the EVEAccount model.
|
Imports an account from the API into the EVEAccount model.
|
||||||
"""
|
"""
|
||||||
print user_id, ":", api_key
|
|
||||||
auth_params = {'userID': user_id, 'apiKey': api_key}
|
auth_params = {'userID': user_id, 'apiKey': api_key}
|
||||||
|
|
||||||
|
try:
|
||||||
account_doc = CachedDocument.objects.api_query('/account/Characters.xml.aspx',
|
account_doc = CachedDocument.objects.api_query('/account/Characters.xml.aspx',
|
||||||
params=auth_params,
|
params=auth_params,
|
||||||
no_cache=False)
|
no_cache=False)
|
||||||
#print account_doc.body
|
except APIAuthException:
|
||||||
|
try:
|
||||||
|
account = EVEAccount.objects.get(id=user_id)
|
||||||
|
except EVEAccount.DoesNotExist:
|
||||||
|
return
|
||||||
|
if api_key == account.api_key:
|
||||||
|
account.api_status = API_STATUS_AUTH_ERROR
|
||||||
|
account.api_last_updated = datetime.utcnow()
|
||||||
|
account.save()
|
||||||
|
return
|
||||||
|
except APINoUserIDException:
|
||||||
|
try:
|
||||||
|
account = EVEAccount.objects.get(id=user_id)
|
||||||
|
account.delete()
|
||||||
|
except EVEAccount.DoesNotExist:
|
||||||
|
return
|
||||||
|
|
||||||
dom = minidom.parseString(account_doc.body)
|
dom = minidom.parseString(account_doc.body.encode('utf-8'))
|
||||||
|
|
||||||
if dom.getElementsByTagName('error'):
|
enode = dom.getElementsByTagName('error')
|
||||||
|
if enode:
|
||||||
try:
|
try:
|
||||||
account = EVEAccount.objects.get(id=user_id)
|
account = EVEAccount.objects.get(id=user_id)
|
||||||
except EVEAccount.DoesNotExist:
|
except EVEAccount.DoesNotExist:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
error = enode[0].getAttribute('code')
|
||||||
|
if error == '211':
|
||||||
|
account.api_status = API_STATUS_ACC_EXPIRED
|
||||||
|
else:
|
||||||
account.api_status = API_STATUS_OTHER_ERROR
|
account.api_status = API_STATUS_OTHER_ERROR
|
||||||
|
account.api_last_updated = datetime.utcnow()
|
||||||
account.save()
|
account.save()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -52,32 +74,83 @@ def import_eve_account(api_key, user_id):
|
|||||||
|
|
||||||
account.api_key = api_key
|
account.api_key = api_key
|
||||||
account.api_user_id = user_id
|
account.api_user_id = user_id
|
||||||
account.api_last_updated = datetime.utcnow()
|
|
||||||
account.api_status = API_STATUS_OK
|
account.api_status = API_STATUS_OK
|
||||||
account.save()
|
|
||||||
|
|
||||||
for node in characters_node_children:
|
for node in characters_node_children:
|
||||||
try:
|
try:
|
||||||
# Get this first, as it's safe.
|
char = import_eve_character(api_key, user_id, node.getAttribute('characterID'))
|
||||||
corporation_id = node.getAttribute('corporationID')
|
if char:
|
||||||
corp, created = EVEPlayerCorporation.objects.get_or_create(id=corporation_id)
|
account.characters.add(char)
|
||||||
# Do this last, since the things we retrieved above are used
|
|
||||||
# on the EVEPlayerCharacter object's fields.
|
|
||||||
character_id = node.getAttribute('characterID')
|
|
||||||
pchar, created = EVEPlayerCharacter.objects.get_or_create(id=character_id)
|
|
||||||
name = node.getAttribute('name')
|
|
||||||
# Save these for last to keep the save count low.
|
|
||||||
pchar.name = name
|
|
||||||
pchar.corporation = corp
|
|
||||||
pchar.api_last_updated = datetime.utcnow()
|
|
||||||
pchar.save()
|
|
||||||
account.characters.add(pchar)
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# This must be a Text node, ignore it.
|
# This must be a Text node, ignore it.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
account.api_last_updated = datetime.utcnow()
|
||||||
|
account.save()
|
||||||
return account
|
return account
|
||||||
|
|
||||||
|
def import_eve_character(api_key, user_id, character_id):
|
||||||
|
|
||||||
|
auth_params = {'userID': user_id, 'apiKey': api_key, 'characterID': character_id }
|
||||||
|
char_doc = CachedDocument.objects.api_query('/char/CharacterSheet.xml.aspx',
|
||||||
|
params=auth_params,
|
||||||
|
no_cache=False)
|
||||||
|
|
||||||
|
dom = minidom.parseString(char_doc.body)
|
||||||
|
if dom.getElementsByTagName('error'):
|
||||||
|
return
|
||||||
|
|
||||||
|
nodes = dom.getElementsByTagName('result')[0].childNodes
|
||||||
|
pchar, created = EVEPlayerCharacter.objects.get_or_create(id=character_id)
|
||||||
|
|
||||||
|
values = {}
|
||||||
|
for node in nodes:
|
||||||
|
if node.nodeType == 1:
|
||||||
|
node.normalize()
|
||||||
|
if len(node.childNodes) == 1:
|
||||||
|
values[node.tagName] = node.childNodes[0].nodeValue
|
||||||
|
else:
|
||||||
|
if node.tagName == "rowset":
|
||||||
|
continue
|
||||||
|
nv = {}
|
||||||
|
for nd in node.childNodes:
|
||||||
|
if nd.nodeType == 1:
|
||||||
|
nv[nd.tagName] = nd.childNodes[0].nodeValue
|
||||||
|
values[node.tagName] = nv
|
||||||
|
|
||||||
|
# Get this first, as it's safe.
|
||||||
|
corporation_id = values['corporationID']
|
||||||
|
corp, created = EVEPlayerCorporation.objects.get_or_create(id=corporation_id)
|
||||||
|
if not corp.name:
|
||||||
|
try:
|
||||||
|
corp.query_and_update_corp()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
name = values['name']
|
||||||
|
# Save these for last to keep the save count low.
|
||||||
|
pchar.name = name
|
||||||
|
pchar.corporation = corp
|
||||||
|
|
||||||
|
pchar.balance = values['balance']
|
||||||
|
pchar.attrib_intelligence = values['attributes']['intelligence']
|
||||||
|
pchar.attrib_charisma = values['attributes']['charisma']
|
||||||
|
pchar.attrib_perception = values['attributes']['perception']
|
||||||
|
pchar.attrib_willpower = values['attributes']['willpower']
|
||||||
|
pchar.attrib_memory = values['attributes']['memory']
|
||||||
|
|
||||||
|
if values['gender'] == 'Male':
|
||||||
|
pchar.gender = 1
|
||||||
|
else:
|
||||||
|
pchar.gender = 2
|
||||||
|
|
||||||
|
pchar.race = API_RACES[values['race']]
|
||||||
|
|
||||||
|
pchar.api_last_updated = datetime.utcnow()
|
||||||
|
pchar.save()
|
||||||
|
|
||||||
|
return pchar
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
"""
|
"""
|
||||||
Test import.
|
Test import.
|
||||||
|
|||||||
@@ -6,10 +6,62 @@ API_STATUS_PENDING = 0
|
|||||||
API_STATUS_OK = 1
|
API_STATUS_OK = 1
|
||||||
API_STATUS_AUTH_ERROR = 2
|
API_STATUS_AUTH_ERROR = 2
|
||||||
API_STATUS_OTHER_ERROR = 3
|
API_STATUS_OTHER_ERROR = 3
|
||||||
|
API_STATUS_ACC_EXPIRED = 4
|
||||||
# This tuple is used to assemble the choices list for the field.
|
# This tuple is used to assemble the choices list for the field.
|
||||||
API_STATUS_CHOICES = (
|
API_STATUS_CHOICES = (
|
||||||
(API_STATUS_PENDING, 'Unknown'),
|
(API_STATUS_PENDING, 'Unknown'),
|
||||||
(API_STATUS_OK, 'OK'),
|
(API_STATUS_OK, 'OK'),
|
||||||
(API_STATUS_AUTH_ERROR, 'Auth Error'),
|
(API_STATUS_AUTH_ERROR, 'Auth Error'),
|
||||||
(API_STATUS_OTHER_ERROR, 'Other Error'),
|
(API_STATUS_OTHER_ERROR, 'Other Error'),
|
||||||
|
(API_STATUS_ACC_EXPIRED, 'Account Expired'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
API_GENDER_CHOICES = (
|
||||||
|
(1, 'Male'),
|
||||||
|
(2, 'Female'),
|
||||||
|
)
|
||||||
|
|
||||||
|
API_RACES_CHOICES = (
|
||||||
|
(1, 'Caldari'),
|
||||||
|
(2, 'Minmatar'),
|
||||||
|
(3, 'Gallente'),
|
||||||
|
(4, 'Amarr'),
|
||||||
|
)
|
||||||
|
|
||||||
|
API_BLOODLINES_CHOICES = (
|
||||||
|
(1, 'Sebiestor'),
|
||||||
|
(2, 'Vherokior'),
|
||||||
|
(3, 'Brutor'),
|
||||||
|
(4, 'Intaki'),
|
||||||
|
(5, 'Gallente'),
|
||||||
|
(6, 'Jin-Mei'),
|
||||||
|
(7, 'Civire'),
|
||||||
|
(8, 'Deteis'),
|
||||||
|
(9, 'Achura'),
|
||||||
|
(10, 'Amarr'),
|
||||||
|
(11, 'Khanid'),
|
||||||
|
(12, 'Ni-Kunni'),
|
||||||
|
(13, 'Caldari'),
|
||||||
|
)
|
||||||
|
|
||||||
|
API_RACES = {
|
||||||
|
'Caldari': 1,
|
||||||
|
'Minmatar': 2,
|
||||||
|
'Gallente': 3,
|
||||||
|
'Amarr': 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
API_BLOODLINES = {
|
||||||
|
'Sebiestor': 1,
|
||||||
|
'Vherokior': 2,
|
||||||
|
'Brutor': 3,
|
||||||
|
'Intaki': 4,
|
||||||
|
'Gallente': 5,
|
||||||
|
'Jin-Mei': 6,
|
||||||
|
'Civire': 7,
|
||||||
|
'Deteis': 8,
|
||||||
|
'Achura': 9,
|
||||||
|
'Amarr': 10,
|
||||||
|
'Khanid': 11,
|
||||||
|
'Ni-Kunni': 12,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import datetime
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
from eve_api.models.api_player import EVEAccount, EVEPlayerCorporation
|
from eve_api.models.api_player import EVEAccount, EVEPlayerCorporation
|
||||||
import eve_api.api_puller.accounts
|
import eve_api.api_puller.accounts
|
||||||
from eve_api.api_exceptions import APIAuthException, APINoUserIDException
|
from eve_api.api_exceptions import APIAuthException, APINoUserIDException
|
||||||
|
|
||||||
class UpdateAPIs(Job):
|
class UpdateAPIs():
|
||||||
"""
|
"""
|
||||||
Updates all Eve API elements in the database
|
Updates all Eve API elements in the database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# run every 2 hours
|
settings = { 'update_corp': False }
|
||||||
run_every = 7200
|
|
||||||
|
last_update_delay = 86400
|
||||||
|
batches = 50
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _logger(self):
|
def _logger(self):
|
||||||
@@ -21,21 +23,23 @@ class UpdateAPIs(Job):
|
|||||||
|
|
||||||
def job(self):
|
def job(self):
|
||||||
# Update all the eve accounts and related corps
|
# Update all the eve accounts and related corps
|
||||||
for acc in EVEAccount.objects.all():
|
|
||||||
|
delta = datetime.timedelta(seconds=self.last_update_delay)
|
||||||
|
self._logger.debug("Updating APIs older than %s" % (datetime.datetime.now() - delta))
|
||||||
|
|
||||||
|
accounts = EVEAccount.objects.filter(api_last_updated__lt=(datetime.datetime.now() - delta))[:self.batches]
|
||||||
|
self._logger.debug("%s account(s) to update" % len(accounts))
|
||||||
|
for acc in accounts:
|
||||||
self._logger.info("Updating UserID %s" % acc.api_user_id)
|
self._logger.info("Updating UserID %s" % acc.api_user_id)
|
||||||
if not acc.user:
|
if not acc.user:
|
||||||
acc.delete()
|
acc.delete()
|
||||||
continue
|
continue
|
||||||
try:
|
|
||||||
eve_api.api_puller.accounts.import_eve_account(acc.api_key, acc.api_user_id)
|
eve_api.api_puller.accounts.import_eve_account(acc.api_key, acc.api_user_id)
|
||||||
acc.api_status = 1
|
|
||||||
except APIAuthException:
|
|
||||||
acc.api_status = 2
|
|
||||||
|
|
||||||
acc.save()
|
|
||||||
|
|
||||||
|
if self.settings['update_corp']:
|
||||||
for corp in EVEPlayerCorporation.objects.all():
|
for corp in EVEPlayerCorporation.objects.all():
|
||||||
|
try:
|
||||||
corp.query_and_update_corp()
|
corp.query_and_update_corp()
|
||||||
|
except:
|
||||||
|
self._logger.error('Error updating %s' % corp)
|
||||||
cronScheduler.register(UpdateAPIs)
|
continue
|
||||||
|
|||||||
1
eve_api/evolutions/__init__.py
Normal file
1
eve_api/evolutions/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SEQUENCE = ['applications-field']
|
||||||
7
eve_api/evolutions/applications-field.py
Normal file
7
eve_api/evolutions/applications-field.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from django_evolution.mutations import *
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
MUTATIONS = [
|
||||||
|
AddField('EVEPlayerCorporation', 'applications', models.BooleanField, initial=False)
|
||||||
|
]
|
||||||
|
|
||||||
@@ -79,10 +79,9 @@ class EVEPlayerCorporationManager(models.Manager):
|
|||||||
"""
|
"""
|
||||||
corp_doc = CachedDocument.objects.api_query('/corp/CorporationSheet.xml.aspx',
|
corp_doc = CachedDocument.objects.api_query('/corp/CorporationSheet.xml.aspx',
|
||||||
params={'corporationID': id})
|
params={'corporationID': id})
|
||||||
corp_dat = corp_doc.body.decode("utf-8", "replace")
|
|
||||||
|
|
||||||
# Convert incoming data to UTF-8.
|
# Convert incoming data to UTF-8.
|
||||||
dom = minidom.parseString(corp_dat)
|
dom = minidom.parseString(corp_doc.body.encode('utf-8'))
|
||||||
|
|
||||||
error_node = dom.getElementsByTagName('error')
|
error_node = dom.getElementsByTagName('error')
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from django.db import models
|
|||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
from eve_proxy.models import CachedDocument
|
from eve_proxy.models import CachedDocument
|
||||||
from eve_api.managers import EVEPlayerCorporationManager, EVEPlayerAllianceManager, EVEPlayerCharacterManager
|
from eve_api.managers import EVEPlayerCorporationManager, EVEPlayerAllianceManager, EVEPlayerCharacterManager
|
||||||
from eve_api.app_defines import API_STATUS_CHOICES, API_STATUS_PENDING
|
from eve_api.app_defines import API_STATUS_CHOICES, API_STATUS_PENDING, API_RACES_CHOICES, API_GENDER_CHOICES
|
||||||
|
|
||||||
class EVEAPIModel(models.Model):
|
class EVEAPIModel(models.Model):
|
||||||
"""
|
"""
|
||||||
@@ -39,6 +39,12 @@ class EVEAccount(EVEAPIModel):
|
|||||||
verbose_name="API Status",
|
verbose_name="API Status",
|
||||||
help_text="End result of the last attempt at updating this object from the API.")
|
help_text="End result of the last attempt at updating this object from the API.")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def api_status_description(self):
|
||||||
|
for choice in API_STATUS_CHOICES:
|
||||||
|
if choice[0] == self.api_status:
|
||||||
|
return choice[1]
|
||||||
|
|
||||||
def in_corp(self, corpid):
|
def in_corp(self, corpid):
|
||||||
for char in self.characters.all():
|
for char in self.characters.all():
|
||||||
if char.corporation_id == corpid:
|
if char.corporation_id == corpid:
|
||||||
@@ -58,10 +64,8 @@ class EVEPlayerCharacter(EVEAPIModel):
|
|||||||
"""
|
"""
|
||||||
name = models.CharField(max_length=255, blank=True, null=False)
|
name = models.CharField(max_length=255, blank=True, null=False)
|
||||||
corporation = models.ForeignKey('EVEPlayerCorporation', blank=True, null=True)
|
corporation = models.ForeignKey('EVEPlayerCorporation', blank=True, null=True)
|
||||||
# TODO: Choices field
|
race = models.IntegerField(blank=True, null=True, choices=API_RACES_CHOICES)
|
||||||
race = models.IntegerField(blank=True, null=True)
|
gender = models.IntegerField(blank=True, null=True, choices=API_GENDER_CHOICES)
|
||||||
# TODO: Choices field
|
|
||||||
gender = models.IntegerField(blank=True, null=True)
|
|
||||||
balance = models.FloatField("Account Balance", blank=True, null=True)
|
balance = models.FloatField("Account Balance", blank=True, null=True)
|
||||||
attrib_intelligence = models.IntegerField("Intelligence", blank=True,
|
attrib_intelligence = models.IntegerField("Intelligence", blank=True,
|
||||||
null=True)
|
null=True)
|
||||||
@@ -72,6 +76,18 @@ class EVEPlayerCharacter(EVEAPIModel):
|
|||||||
|
|
||||||
objects = EVEPlayerCharacterManager()
|
objects = EVEPlayerCharacterManager()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gender_description(self):
|
||||||
|
for choice in API_GENDER_CHOICES:
|
||||||
|
if choice[0] == self.gender:
|
||||||
|
return choice[1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def race_description(self):
|
||||||
|
for choice in API_RACES_CHOICES:
|
||||||
|
if choice[0] == self.race:
|
||||||
|
return choice[1]
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
if self.name:
|
if self.name:
|
||||||
return "%s (%d)" % (self.name, self.id)
|
return "%s (%d)" % (self.name, self.id)
|
||||||
@@ -141,6 +157,7 @@ class EVEPlayerCorporation(EVEAPIModel):
|
|||||||
logo_color3 = models.IntegerField(blank=True, null=True)
|
logo_color3 = models.IntegerField(blank=True, null=True)
|
||||||
|
|
||||||
group = models.ForeignKey(Group, blank=True, null=True)
|
group = models.ForeignKey(Group, blank=True, null=True)
|
||||||
|
applications = models.BooleanField(blank=False, default=False)
|
||||||
|
|
||||||
objects = EVEPlayerCorporationManager()
|
objects = EVEPlayerCorporationManager()
|
||||||
|
|
||||||
@@ -192,9 +209,7 @@ class EVEPlayerCorporation(EVEAPIModel):
|
|||||||
continue
|
continue
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# Something weird has happened
|
# Something weird has happened
|
||||||
print " * Index Error:", tag_map[0]
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print "Updating", self.id, self.name
|
|
||||||
self.api_last_updated = datetime.utcnow()
|
self.api_last_updated = datetime.utcnow()
|
||||||
self.save()
|
self.save()
|
||||||
|
|||||||
@@ -27,14 +27,12 @@ class CachedDocumentManager(models.Manager):
|
|||||||
# Retrieve the response from the server.
|
# Retrieve the response from the server.
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
# Save the response (an XML document) to the CachedDocument.
|
# Save the response (an XML document) to the CachedDocument.
|
||||||
cached_doc.body = response.read()
|
cached_doc.body = unicode(response.read(), 'utf-8')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Parse the response via minidom
|
# Parse the response via minidom
|
||||||
dom = minidom.parseString(cached_doc.body)
|
dom = minidom.parseString(cached_doc.body.encode('utf-8'))
|
||||||
except xml.parsers.expat.ExpatError:
|
except xml.parsers.expat.ExpatError:
|
||||||
print "XML Parser Error:"
|
|
||||||
print cached_doc.body
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set the CachedDocument's time_retrieved and cached_until times based
|
# Set the CachedDocument's time_retrieved and cached_until times based
|
||||||
@@ -97,7 +95,7 @@ class CachedDocumentManager(models.Manager):
|
|||||||
else:
|
else:
|
||||||
# Parse the document here since it was retrieved from the
|
# Parse the document here since it was retrieved from the
|
||||||
# database cache instead of queried for.
|
# database cache instead of queried for.
|
||||||
dom = minidom.parseString(cached_doc.body)
|
dom = minidom.parseString(cached_doc.body.encode('utf-8'))
|
||||||
|
|
||||||
# Check for the presence errors. Only check the bare minimum,
|
# Check for the presence errors. Only check the bare minimum,
|
||||||
# generic stuff that applies to most or all queries. User-level code
|
# generic stuff that applies to most or all queries. User-level code
|
||||||
|
|||||||
0
hr/__init__.py
Normal file
0
hr/__init__.py
Normal file
17
hr/admin.py
Normal file
17
hr/admin.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
from hr.models import Application, Recommendation
|
||||||
|
|
||||||
|
class ApplicationAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('user', 'character', 'status')
|
||||||
|
search_fields = ['user', 'character', 'status']
|
||||||
|
|
||||||
|
admin.site.register(Application, ApplicationAdmin)
|
||||||
|
|
||||||
|
class RecommendationAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('user', 'user_character')
|
||||||
|
search_fields = ['user_character']
|
||||||
|
|
||||||
|
admin.site.register(Recommendation, RecommendationAdmin)
|
||||||
|
|
||||||
16
hr/app_defines.py
Normal file
16
hr/app_defines.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
APPLICATION_STATUS_NOTSUBMITTED = 0
|
||||||
|
APPLICATION_STATUS_AWAITINGREVIEW = 1
|
||||||
|
APPLICATION_STATUS_REJECTED = 2
|
||||||
|
APPLICATION_STATUS_ACCEPTED = 3
|
||||||
|
APPLICATION_STATUS_QUERY = 4
|
||||||
|
APPLICATION_STATUS_COMPLETED = 5
|
||||||
|
|
||||||
|
APPLICATION_STATUS_CHOICES = (
|
||||||
|
(APPLICATION_STATUS_NOTSUBMITTED, 'Not Submitted'),
|
||||||
|
(APPLICATION_STATUS_AWAITINGREVIEW, 'Awaiting Review'),
|
||||||
|
(APPLICATION_STATUS_REJECTED, 'Rejected'),
|
||||||
|
(APPLICATION_STATUS_ACCEPTED, 'Accepted'),
|
||||||
|
(APPLICATION_STATUS_QUERY, 'In Query'),
|
||||||
|
(APPLICATION_STATUS_COMPLETED, 'Completed'),
|
||||||
|
)
|
||||||
40
hr/forms.py
Normal file
40
hr/forms.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from django import forms
|
||||||
|
import settings
|
||||||
|
|
||||||
|
from hr.app_defines import *
|
||||||
|
from hr.models import Application
|
||||||
|
from eve_api.models import EVEPlayerCharacter, EVEPlayerCorporation
|
||||||
|
|
||||||
|
def CreateRecommendationForm(user):
|
||||||
|
""" Generate a Recommendation form based on the user's permissions """
|
||||||
|
|
||||||
|
characters = EVEPlayerCharacter.objects.filter(eveaccount__user=user)
|
||||||
|
applications = Application.objects.filter(status=APPLICATION_STATUS_NOTSUBMITTED)
|
||||||
|
|
||||||
|
class RecommendationForm(forms.Form):
|
||||||
|
""" Service Account Form """
|
||||||
|
|
||||||
|
character = forms.ModelChoiceField(queryset=characters, required=True, empty_label=None)
|
||||||
|
application = forms.ModelChoiceField(queryset=applications, required=True, empty_label=None)
|
||||||
|
|
||||||
|
return RecommendationForm
|
||||||
|
|
||||||
|
|
||||||
|
def CreateApplicationForm(user):
|
||||||
|
""" Generate a Application form based on the user's permissions """
|
||||||
|
|
||||||
|
characters = EVEPlayerCharacter.objects.filter(eveaccount__user=user)
|
||||||
|
corporations = EVEPlayerCorporation.objects.filter(applications=True)
|
||||||
|
|
||||||
|
class ApplicationForm(forms.Form):
|
||||||
|
character = forms.ModelChoiceField(queryset=characters, required=True, empty_label=None)
|
||||||
|
corporation = forms.ModelChoiceField(queryset=corporations, required=True, empty_label=None)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if len(Application.objects.filter(character=self.cleaned_data['character'], status__in=[APPLICATION_STATUS_NOTSUBMITTED, APPLICATION_STATUS_AWAITINGREVIEW, APPLICATION_STATUS_QUERY])):
|
||||||
|
raise forms.ValidationError("This character already has a open application")
|
||||||
|
|
||||||
|
return self.cleaned_data
|
||||||
|
return ApplicationForm
|
||||||
|
|
||||||
|
|
||||||
40
hr/models.py
Normal file
40
hr/models.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from eve_api.models import EVEPlayerCharacter, EVEPlayerCorporation
|
||||||
|
|
||||||
|
from hr.app_defines import *
|
||||||
|
|
||||||
|
class Application(models.Model):
|
||||||
|
user = models.ForeignKey(User, blank=False, verbose_name="User")
|
||||||
|
character = models.ForeignKey(EVEPlayerCharacter, blank=False, verbose_name="Character")
|
||||||
|
corporation = models.ForeignKey(EVEPlayerCorporation, blank=False, verbose_name="Applying to Corporation")
|
||||||
|
status = models.IntegerField(choices=APPLICATION_STATUS_CHOICES,
|
||||||
|
default=APPLICATION_STATUS_NOTSUBMITTED,
|
||||||
|
verbose_name="Status",
|
||||||
|
help_text="Current status of this application request.")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def status_description(self):
|
||||||
|
for choice in APPLICATION_STATUS_CHOICES:
|
||||||
|
if choice[0] == self.status:
|
||||||
|
return choice[1]
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.character.name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.__unicode__()
|
||||||
|
|
||||||
|
class Recommendation(models.Model):
|
||||||
|
user = models.ForeignKey(User, blank=False, verbose_name="User")
|
||||||
|
user_character = models.ForeignKey(EVEPlayerCharacter, blank=False, verbose_name="Recommender")
|
||||||
|
application = models.ForeignKey(Application, blank=False, verbose_name="Recommended Application")
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.user_character.name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.__unicode__()
|
||||||
23
hr/tests.py
Normal file
23
hr/tests.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
This file demonstrates two different styles of tests (one doctest and one
|
||||||
|
unittest). These will both pass when you run "manage.py test".
|
||||||
|
|
||||||
|
Replace these with more appropriate tests for your application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
class SimpleTest(TestCase):
|
||||||
|
def test_basic_addition(self):
|
||||||
|
"""
|
||||||
|
Tests that 1 + 1 always equals 2.
|
||||||
|
"""
|
||||||
|
self.failUnlessEqual(1 + 1, 2)
|
||||||
|
|
||||||
|
__test__ = {"doctest": """
|
||||||
|
Another way to test that 1 + 1 is equal to 2.
|
||||||
|
|
||||||
|
>>> 1 + 1 == 2
|
||||||
|
True
|
||||||
|
"""}
|
||||||
|
|
||||||
14
hr/urls.py
Normal file
14
hr/urls.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
from hr import views
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
('^$', views.index),
|
||||||
|
(r'^recommendations/$', views.view_recommendations),
|
||||||
|
(r'^recommendations/(?P<recommendationid>.*)/$', views.view_recommendation),
|
||||||
|
(r'^applications/$', views.view_applications),
|
||||||
|
(r'^applications/(?P<applicationid>.*)/$', views.view_application),
|
||||||
|
|
||||||
|
(r'^add/application/$', views.add_application),
|
||||||
|
(r'^add/recommendation/$', views.add_recommendation),
|
||||||
|
)
|
||||||
118
hr/views.py
Normal file
118
hr/views.py
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.template import RequestContext
|
||||||
|
|
||||||
|
import settings
|
||||||
|
|
||||||
|
from eve_api.models import EVEAccount, EVEPlayerCorporation
|
||||||
|
from reddit.models import RedditAccount
|
||||||
|
|
||||||
|
from hr.forms import CreateRecommendationForm, CreateApplicationForm
|
||||||
|
from hr.models import Recommendation, Application
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
if request.user.is_staff or settings.HR_STAFF_GROUP in request.user.groups.all():
|
||||||
|
hrstaff = True
|
||||||
|
|
||||||
|
return render_to_response('hr/index.html', locals(), context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
### Application Management
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def view_applications(request):
|
||||||
|
apps = Application.objects.filter(user=request.user)
|
||||||
|
return render_to_response('hr/applications/view_list.html', locals(), context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def view_application(request, applicationid):
|
||||||
|
try:
|
||||||
|
app = Application.objects.get(id=applicationid)
|
||||||
|
except Application.DoesNotExist:
|
||||||
|
return HttpResponseRedirect(reverse('hr.views.index'))
|
||||||
|
|
||||||
|
if not app.user == request.user and not (request.user.is_staff or settings.HR_STAFF_GROUP in request.user.groups.all()):
|
||||||
|
return HttpResponseRedirect(reverse('hr.views.index'))
|
||||||
|
|
||||||
|
eveacc = EVEAccount.objects.filter(user=app.user)
|
||||||
|
redditacc = RedditAccount.objects.filter(user=app.user)
|
||||||
|
recs = Recommendation.objects.filter(application=app)
|
||||||
|
|
||||||
|
posts = []
|
||||||
|
for acc in redditacc:
|
||||||
|
posts.extend(acc.recent_posts())
|
||||||
|
|
||||||
|
return render_to_response('hr/applications/view.html', locals(), context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def add_application(request):
|
||||||
|
|
||||||
|
clsform = CreateApplicationForm(request.user)
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = clsform(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
app = Application()
|
||||||
|
|
||||||
|
app.user = request.user
|
||||||
|
app.character = form.cleaned_data['character']
|
||||||
|
app.corporation = form.cleaned_data['corporation']
|
||||||
|
app.save()
|
||||||
|
|
||||||
|
request.user.message_set.create(message="Your application to %s has been submitted." % app.corporation)
|
||||||
|
return HttpResponseRedirect(reverse('hr.views.view_applications'))
|
||||||
|
|
||||||
|
else:
|
||||||
|
form = clsform() # An unbound form
|
||||||
|
|
||||||
|
if len(EVEPlayerCorporation.objects.filter(applications=True)):
|
||||||
|
return render_to_response('hr/applications/add.html', locals(), context_instance=RequestContext(request))
|
||||||
|
else:
|
||||||
|
return render_to_response('hr/applications/noadd.html', locals(), context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
### Recommendation Management
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def view_recommendations(request):
|
||||||
|
recs = Recommendation.objects.filter(user=request.user, application__status=0)
|
||||||
|
return render_to_response('hr/recommendations/view_list.html', locals(), context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def view_recommendation(request, recommendationid):
|
||||||
|
try:
|
||||||
|
rec = Recommendation.objects.get(id=recommendationid, user=request.user)
|
||||||
|
except Recommendation.DoesNotExist:
|
||||||
|
return HttpResponseRedirect(reverse('hr.views.index'))
|
||||||
|
return render_to_response('hr/recommendations/view.html', locals(), context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def add_recommendation(request):
|
||||||
|
|
||||||
|
clsform = CreateRecommendationForm(request.user)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = clsform(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
rec = Recommendation()
|
||||||
|
|
||||||
|
rec.user = request.user
|
||||||
|
rec.user_character = form.cleaned_data['character']
|
||||||
|
rec.application = form.cleaned_data['application']
|
||||||
|
rec.created_by = request.user
|
||||||
|
rec.last_updated_by = request.user
|
||||||
|
rec.save()
|
||||||
|
|
||||||
|
request.user.message_set.create(message="Recommendation added to %s's application" % rec.application )
|
||||||
|
return HttpResponseRedirect(reverse('hr.views.view_recommendations'))
|
||||||
|
|
||||||
|
else:
|
||||||
|
form = clsform() # An unbound form
|
||||||
|
|
||||||
|
return render_to_response('hr/recommendations/add.html', locals(), context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1
init.sh
1
init.sh
@@ -1,6 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
svn checkout http://django-registration.googlecode.com/svn/trunk/registration
|
|
||||||
svn co http://code.djangoproject.com/svn/django/branches/releases/1.1.X/django django
|
svn co http://code.djangoproject.com/svn/django/branches/releases/1.1.X/django django
|
||||||
svn co http://django-evolution.googlecode.com/svn/trunk/django_evolution
|
svn co http://django-evolution.googlecode.com/svn/trunk/django_evolution
|
||||||
hg clone http://bitbucket.org/jespern/django-piston/
|
hg clone http://bitbucket.org/jespern/django-piston/
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class MumbleCtlDbus_118(MumbleCtlBase):
|
|||||||
info[str(key)] = conf[key];
|
info[str(key)] = conf[key];
|
||||||
return info;
|
return info;
|
||||||
|
|
||||||
def getConf(self, srvid, key, value):
|
def getConf(self, srvid, key):
|
||||||
if key == "username":
|
if key == "username":
|
||||||
key = "playername";
|
key = "playername";
|
||||||
|
|
||||||
@@ -125,9 +125,10 @@ class MumbleCtlDbus_118(MumbleCtlBase):
|
|||||||
ret = {};
|
ret = {};
|
||||||
|
|
||||||
for channel in chans:
|
for channel in chans:
|
||||||
|
print channel;
|
||||||
ret[ channel[0] ] = ObjectInfo(
|
ret[ channel[0] ] = ObjectInfo(
|
||||||
id = int(channel[0]),
|
id = int(channel[0]),
|
||||||
name = str(channel[1]),
|
name = unicode(channel[1]),
|
||||||
parent = int(channel[2]),
|
parent = int(channel[2]),
|
||||||
links = [ int(lnk) for lnk in channel[3] ],
|
links = [ int(lnk) for lnk in channel[3] ],
|
||||||
);
|
);
|
||||||
@@ -149,7 +150,7 @@ class MumbleCtlDbus_118(MumbleCtlBase):
|
|||||||
selfDeaf = bool( playerObj[5] ),
|
selfDeaf = bool( playerObj[5] ),
|
||||||
channel = int( playerObj[6] ),
|
channel = int( playerObj[6] ),
|
||||||
userid = int( playerObj[7] ),
|
userid = int( playerObj[7] ),
|
||||||
name = str( playerObj[8] ),
|
name = unicode( playerObj[8] ),
|
||||||
onlinesecs = int( playerObj[9] ),
|
onlinesecs = int( playerObj[9] ),
|
||||||
bytespersec = int( playerObj[10] )
|
bytespersec = int( playerObj[10] )
|
||||||
);
|
);
|
||||||
@@ -178,7 +179,7 @@ class MumbleCtlDbus_118(MumbleCtlBase):
|
|||||||
applySubs = bool(rule[1]),
|
applySubs = bool(rule[1]),
|
||||||
inherited = bool(rule[2]),
|
inherited = bool(rule[2]),
|
||||||
userid = int(rule[3]),
|
userid = int(rule[3]),
|
||||||
group = str(rule[4]),
|
group = unicode(rule[4]),
|
||||||
allow = int(rule[5]),
|
allow = int(rule[5]),
|
||||||
deny = int(rule[6]),
|
deny = int(rule[6]),
|
||||||
)
|
)
|
||||||
@@ -186,7 +187,7 @@ class MumbleCtlDbus_118(MumbleCtlBase):
|
|||||||
];
|
];
|
||||||
|
|
||||||
groups = [ ObjectInfo(
|
groups = [ ObjectInfo(
|
||||||
name = str(group[0]),
|
name = unicode(group[0]),
|
||||||
inherited = bool(group[1]),
|
inherited = bool(group[1]),
|
||||||
inherit = bool(group[2]),
|
inherit = bool(group[2]),
|
||||||
inheritable = bool(group[3]),
|
inheritable = bool(group[3]),
|
||||||
@@ -261,7 +262,7 @@ class MumbleCtlDbus_118(MumbleCtlBase):
|
|||||||
|
|
||||||
def setTexture(self, srvid, mumbleid, infile):
|
def setTexture(self, srvid, mumbleid, infile):
|
||||||
# open image, convert to RGBA, and resize to 600x60
|
# open image, convert to RGBA, and resize to 600x60
|
||||||
img = Image.open( infile ).convert( "RGBA" ).transform( ( 600, 60 ), Image.EXTENT, ( 0, 0, 600, 60 ) );
|
img = infile.convert( "RGBA" ).transform( ( 600, 60 ), Image.EXTENT, ( 0, 0, 600, 60 ) );
|
||||||
# iterate over the list and pack everything into a string
|
# iterate over the list and pack everything into a string
|
||||||
bgrastring = "";
|
bgrastring = "";
|
||||||
for ent in list( img.getdata() ):
|
for ent in list( img.getdata() ):
|
||||||
|
|||||||
@@ -15,16 +15,20 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from os.path import exists
|
from time import time
|
||||||
|
from functools import wraps
|
||||||
|
from StringIO import StringIO
|
||||||
|
from os.path import exists, join
|
||||||
|
from os import unlink, name as os_name
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from struct import pack, unpack
|
from struct import pack, unpack
|
||||||
from zlib import compress, decompress
|
from zlib import compress, decompress, error
|
||||||
|
|
||||||
from mctl import MumbleCtlBase
|
from mctl import MumbleCtlBase
|
||||||
|
|
||||||
from utils import ObjectInfo
|
from utils import ObjectInfo
|
||||||
|
|
||||||
import Ice
|
import Ice, IcePy, tempfile
|
||||||
|
|
||||||
|
|
||||||
def protectDjangoErrPage( func ):
|
def protectDjangoErrPage( func ):
|
||||||
@@ -36,46 +40,103 @@ def protectDjangoErrPage( func ):
|
|||||||
non-existant files and borking.
|
non-existant files and borking.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def protection_wrapper( *args, **kwargs ):
|
@wraps(func)
|
||||||
|
def protection_wrapper( self, *args, **kwargs ):
|
||||||
""" Call the original function and catch Ice exceptions. """
|
""" Call the original function and catch Ice exceptions. """
|
||||||
try:
|
try:
|
||||||
return func( *args, **kwargs );
|
return func( self, *args, **kwargs );
|
||||||
except Ice.Exception, e:
|
except Ice.Exception, err:
|
||||||
raise e;
|
raise err;
|
||||||
protection_wrapper.innerfunc = func
|
protection_wrapper.innerfunc = func
|
||||||
|
|
||||||
return protection_wrapper;
|
return protection_wrapper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@protectDjangoErrPage
|
@protectDjangoErrPage
|
||||||
def MumbleCtlIce( connstring, slicefile ):
|
def MumbleCtlIce( connstring, slicefile=None, icesecret=None ):
|
||||||
""" Choose the correct Ice handler to use (1.1.8 or 1.2.x), and make sure the
|
""" Choose the correct Ice handler to use (1.1.8 or 1.2.x), and make sure the
|
||||||
Murmur version matches the slice Version.
|
Murmur version matches the slice Version.
|
||||||
|
|
||||||
|
Optional parameters are the path to the slice file and the Ice secret
|
||||||
|
necessary to authenticate to Murmur.
|
||||||
|
|
||||||
|
The path can be omitted only if running Murmur 1.2.3 or later, which
|
||||||
|
exports a getSlice method to retrieve the Slice from.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
prop = Ice.createProperties([])
|
||||||
|
prop.setProperty("Ice.ImplicitContext", "Shared")
|
||||||
|
|
||||||
|
idd = Ice.InitializationData()
|
||||||
|
idd.properties = prop
|
||||||
|
|
||||||
|
ice = Ice.initialize(idd)
|
||||||
|
|
||||||
|
if icesecret:
|
||||||
|
ice.getImplicitContext().put( "secret", icesecret.encode("utf-8") )
|
||||||
|
|
||||||
|
prx = ice.stringToProxy( connstring.encode("utf-8") )
|
||||||
|
|
||||||
|
try:
|
||||||
|
prx.ice_ping()
|
||||||
|
except Ice.Exception:
|
||||||
|
raise EnvironmentError( "Murmur does not appear to be listening on this address (Ice ping failed)." )
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import Murmur
|
import Murmur
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
# Try loading the Slice from Murmur directly via its getSlice method.
|
||||||
|
# See scripts/testdynamic.py in Mumble's Git repository.
|
||||||
|
try:
|
||||||
|
slice = IcePy.Operation( 'getSlice',
|
||||||
|
Ice.OperationMode.Idempotent, Ice.OperationMode.Idempotent,
|
||||||
|
True, (), (), (), IcePy._t_string, ()
|
||||||
|
).invoke(prx, ((), None))
|
||||||
|
except (TypeError, Ice.OperationNotExistException):
|
||||||
if not slicefile:
|
if not slicefile:
|
||||||
raise EnvironmentError( "You didn't configure a slice file. Please set the SLICE variable in settings.py." )
|
raise EnvironmentError(
|
||||||
|
"You didn't configure a slice file. Please set the SLICE variable in settings.py." )
|
||||||
if not exists( slicefile ):
|
if not exists( slicefile ):
|
||||||
raise EnvironmentError( "The slice file does not exist: '%s' - please check the settings." % slicefile )
|
raise EnvironmentError(
|
||||||
|
"The slice file does not exist: '%s' - please check the settings." % slicefile )
|
||||||
if " " in slicefile:
|
if " " in slicefile:
|
||||||
raise EnvironmentError( "You have a space char in your Slice path. This will confuse Ice, please check." )
|
raise EnvironmentError(
|
||||||
|
"You have a space char in your Slice path. This will confuse Ice, please check." )
|
||||||
if not slicefile.endswith( ".ice" ):
|
if not slicefile.endswith( ".ice" ):
|
||||||
raise EnvironmentError( "The slice file name MUST end with '.ice'." )
|
raise EnvironmentError( "The slice file name MUST end with '.ice'." )
|
||||||
|
|
||||||
|
try:
|
||||||
Ice.loadSlice( slicefile )
|
Ice.loadSlice( slicefile )
|
||||||
|
except RuntimeError:
|
||||||
|
raise RuntimeError( "Slice preprocessing failed. Please check your server's error log." )
|
||||||
|
else:
|
||||||
|
if os_name == "nt":
|
||||||
|
# It weren't Windows if it didn't need to be treated differently. *sigh*
|
||||||
|
temppath = join( tempfile.gettempdir(), "Murmur.ice" )
|
||||||
|
slicetemp = open( temppath, "w+b" )
|
||||||
|
try:
|
||||||
|
slicetemp.write( slice )
|
||||||
|
finally:
|
||||||
|
slicetemp.close()
|
||||||
|
try:
|
||||||
|
Ice.loadSlice( temppath )
|
||||||
|
except RuntimeError:
|
||||||
|
raise RuntimeError( "Slice preprocessing failed. Please check your server's error log." )
|
||||||
|
finally:
|
||||||
|
unlink(temppath)
|
||||||
|
else:
|
||||||
|
slicetemp = tempfile.NamedTemporaryFile( suffix='.ice' )
|
||||||
|
try:
|
||||||
|
slicetemp.write( slice )
|
||||||
|
slicetemp.flush()
|
||||||
|
Ice.loadSlice( slicetemp.name )
|
||||||
|
except RuntimeError:
|
||||||
|
raise RuntimeError( "Slice preprocessing failed. Please check your server's error log." )
|
||||||
|
finally:
|
||||||
|
slicetemp.close()
|
||||||
|
|
||||||
import Murmur
|
import Murmur
|
||||||
|
|
||||||
ice = Ice.initialize()
|
|
||||||
prx = ice.stringToProxy( connstring.encode("utf-8") )
|
|
||||||
meta = Murmur.MetaPrx.checkedCast(prx)
|
meta = Murmur.MetaPrx.checkedCast(prx)
|
||||||
|
|
||||||
murmurversion = meta.getVersion()[:3]
|
murmurversion = meta.getVersion()[:3]
|
||||||
@@ -84,8 +145,17 @@ def MumbleCtlIce( connstring, slicefile ):
|
|||||||
return MumbleCtlIce_118( connstring, meta );
|
return MumbleCtlIce_118( connstring, meta );
|
||||||
|
|
||||||
elif murmurversion[:2] == (1, 2):
|
elif murmurversion[:2] == (1, 2):
|
||||||
|
if murmurversion[2] < 2:
|
||||||
return MumbleCtlIce_120( connstring, meta );
|
return MumbleCtlIce_120( connstring, meta );
|
||||||
|
|
||||||
|
elif murmurversion[2] == 2:
|
||||||
|
return MumbleCtlIce_122( connstring, meta );
|
||||||
|
|
||||||
|
elif murmurversion[2] == 3:
|
||||||
|
return MumbleCtlIce_123( connstring, meta );
|
||||||
|
|
||||||
|
raise NotImplementedError( "No ctl object available for Murmur version %d.%d.%d" % tuple(murmurversion) )
|
||||||
|
|
||||||
|
|
||||||
class MumbleCtlIce_118(MumbleCtlBase):
|
class MumbleCtlIce_118(MumbleCtlBase):
|
||||||
method = "ICE";
|
method = "ICE";
|
||||||
@@ -291,7 +361,10 @@ class MumbleCtlIce_118(MumbleCtlBase):
|
|||||||
if len(texture) == 0:
|
if len(texture) == 0:
|
||||||
raise ValueError( "No Texture has been set." );
|
raise ValueError( "No Texture has been set." );
|
||||||
# this returns a list of bytes.
|
# this returns a list of bytes.
|
||||||
|
try:
|
||||||
decompressed = decompress( texture );
|
decompressed = decompress( texture );
|
||||||
|
except error, err:
|
||||||
|
raise ValueError( err )
|
||||||
# iterate over 4 byte chunks of the string
|
# iterate over 4 byte chunks of the string
|
||||||
imgdata = "";
|
imgdata = "";
|
||||||
for idx in range( 0, len(decompressed), 4 ):
|
for idx in range( 0, len(decompressed), 4 ):
|
||||||
@@ -308,7 +381,7 @@ class MumbleCtlIce_118(MumbleCtlBase):
|
|||||||
@protectDjangoErrPage
|
@protectDjangoErrPage
|
||||||
def setTexture(self, srvid, mumbleid, infile):
|
def setTexture(self, srvid, mumbleid, infile):
|
||||||
# open image, convert to RGBA, and resize to 600x60
|
# open image, convert to RGBA, and resize to 600x60
|
||||||
img = Image.open( infile ).convert( "RGBA" ).transform( ( 600, 60 ), Image.EXTENT, ( 0, 0, 600, 60 ) );
|
img = infile.convert( "RGBA" ).transform( ( 600, 60 ), Image.EXTENT, ( 0, 0, 600, 60 ) );
|
||||||
# iterate over the list and pack everything into a string
|
# iterate over the list and pack everything into a string
|
||||||
bgrastring = "";
|
bgrastring = "";
|
||||||
for ent in list( img.getdata() ):
|
for ent in list( img.getdata() ):
|
||||||
@@ -360,7 +433,20 @@ class MumbleCtlIce_120(MumbleCtlIce_118):
|
|||||||
|
|
||||||
@protectDjangoErrPage
|
@protectDjangoErrPage
|
||||||
def getPlayers(self, srvid):
|
def getPlayers(self, srvid):
|
||||||
return self._getIceServerObject(srvid).getUsers();
|
userdata = self._getIceServerObject(srvid).getUsers();
|
||||||
|
for key in userdata:
|
||||||
|
if isinstance( userdata[key], str ):
|
||||||
|
userdata[key] = userdata[key].decode( "UTF-8" )
|
||||||
|
return userdata
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def getState(self, srvid, sessionid):
|
||||||
|
userdata = self._getIceServerObject(srvid).getState(sessionid);
|
||||||
|
for key in userdata.__dict__:
|
||||||
|
attr = getattr( userdata, key )
|
||||||
|
if isinstance( attr, str ):
|
||||||
|
setattr( userdata, key, attr.decode( "UTF-8" ) )
|
||||||
|
return userdata
|
||||||
|
|
||||||
@protectDjangoErrPage
|
@protectDjangoErrPage
|
||||||
def registerPlayer(self, srvid, name, email, password):
|
def registerPlayer(self, srvid, name, email, password):
|
||||||
@@ -434,3 +520,76 @@ class MumbleCtlIce_120(MumbleCtlIce_118):
|
|||||||
def setACL(self, srvid, channelid, acls, groups, inherit):
|
def setACL(self, srvid, channelid, acls, groups, inherit):
|
||||||
return self._getIceServerObject(srvid).setACL( channelid, acls, groups, inherit );
|
return self._getIceServerObject(srvid).setACL( channelid, acls, groups, inherit );
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def getBans(self, srvid):
|
||||||
|
return self._getIceServerObject(srvid).getBans();
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def setBans(self, srvid, bans):
|
||||||
|
return self._getIceServerObject(srvid).setBans(bans);
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def addBanForSession(self, srvid, sessionid, **kwargs):
|
||||||
|
session = self.getState(srvid, sessionid);
|
||||||
|
if "bits" not in kwargs:
|
||||||
|
kwargs["bits"] = 128;
|
||||||
|
if "start" not in kwargs:
|
||||||
|
kwargs["start"] = int(time());
|
||||||
|
if "duration" not in kwargs:
|
||||||
|
kwargs["duration"] = 3600;
|
||||||
|
return self.addBan(srvid, address=session.address, **kwargs);
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def addBan(self, srvid, **kwargs):
|
||||||
|
for key in kwargs:
|
||||||
|
if isinstance( kwargs[key], unicode ):
|
||||||
|
kwargs[key] = kwargs[key].encode("UTF-8")
|
||||||
|
|
||||||
|
from Murmur import Ban
|
||||||
|
srvbans = self.getBans(srvid);
|
||||||
|
srvbans.append( Ban( **kwargs ) );
|
||||||
|
return self.setBans(srvid, srvbans);
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def kickUser(self, srvid, userid, reason=""):
|
||||||
|
return self._getIceServerObject(srvid).kickUser( userid, reason.encode("UTF-8") );
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleCtlIce_122(MumbleCtlIce_120):
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def getTexture(self, srvid, mumbleid):
|
||||||
|
raise ValueError( "This method is buggy in 1.2.2, sorry dude." );
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def setTexture(self, srvid, mumbleid, infile):
|
||||||
|
buf = StringIO()
|
||||||
|
infile.save( buf, "PNG" )
|
||||||
|
buf.seek(0)
|
||||||
|
self._getIceServerObject(srvid).setTexture(mumbleid, buf.read())
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleCtlIce_123(MumbleCtlIce_120):
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def getRawTexture(self, srvid, mumbleid):
|
||||||
|
return self._getIceServerObject(srvid).getTexture(mumbleid)
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def getTexture(self, srvid, mumbleid):
|
||||||
|
texture = self.getRawTexture(srvid, mumbleid)
|
||||||
|
if len(texture) == 0:
|
||||||
|
raise ValueError( "No Texture has been set." );
|
||||||
|
from StringIO import StringIO
|
||||||
|
try:
|
||||||
|
return Image.open( StringIO( texture ) )
|
||||||
|
except IOError, err:
|
||||||
|
raise ValueError( err )
|
||||||
|
|
||||||
|
@protectDjangoErrPage
|
||||||
|
def setTexture(self, srvid, mumbleid, infile):
|
||||||
|
buf = StringIO()
|
||||||
|
infile.save( buf, "PNG" )
|
||||||
|
buf.seek(0)
|
||||||
|
self._getIceServerObject(srvid).setTexture(mumbleid, buf.read())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,27 +14,61 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
#from mumble.forms import MumbleAdminForm, MumbleUserAdminForm
|
from mumble.forms import MumbleServerForm, MumbleAdminForm, MumbleUserAdminForm
|
||||||
from mumble.models import Mumble, MumbleUser
|
from mumble.models import MumbleServer, Mumble, MumbleUser
|
||||||
|
|
||||||
|
class MumbleServerAdmin(admin.ModelAdmin):
|
||||||
|
list_display = [ 'dbus', 'get_murmur_online' ]
|
||||||
|
search_fields = [ 'dbus' ]
|
||||||
|
ordering = [ 'dbus' ]
|
||||||
|
|
||||||
|
form = MumbleServerForm
|
||||||
|
|
||||||
|
def get_murmur_online( self, obj ):
|
||||||
|
return obj.online
|
||||||
|
|
||||||
|
get_murmur_online.short_description = _('Master is running')
|
||||||
|
get_murmur_online.boolean = True
|
||||||
|
|
||||||
|
|
||||||
class MumbleAdmin(admin.ModelAdmin):
|
class MumbleAdmin(admin.ModelAdmin):
|
||||||
""" Specification for the "Server administration" admin section. """
|
""" Specification for the "Server administration" admin section. """
|
||||||
|
|
||||||
list_display = [ 'name', 'addr', 'port', 'get_booted', 'get_is_public',
|
list_display = [ 'name', 'srvid', 'get_addr', 'get_port', 'get_murmur_online', 'get_booted',
|
||||||
'get_users_regged', 'get_users_online', 'get_channel_count' ];
|
'get_is_public', 'get_users_regged', 'get_users_online', 'get_channel_count' ];
|
||||||
list_filter = [ 'addr' ];
|
list_filter = [ 'addr', 'server' ];
|
||||||
search_fields = [ 'name', 'addr', 'port' ];
|
search_fields = [ 'name', 'addr', 'port' ];
|
||||||
ordering = [ 'name' ];
|
ordering = [ 'name' ];
|
||||||
#form = MumbleAdminForm;
|
form = MumbleAdminForm;
|
||||||
|
|
||||||
|
def get_murmur_online( self, obj ):
|
||||||
|
return obj.server.online
|
||||||
|
|
||||||
|
get_murmur_online.short_description = _('Master is running')
|
||||||
|
get_murmur_online.boolean = True
|
||||||
|
|
||||||
|
def get_addr( self, obj ):
|
||||||
|
if not obj.addr:
|
||||||
|
return "*"
|
||||||
|
return obj.addr
|
||||||
|
|
||||||
|
get_addr.short_description = _('Server Address')
|
||||||
|
|
||||||
|
def get_port( self, obj ):
|
||||||
|
if not obj.port:
|
||||||
|
return "< %d >" % (settings.MUMBLE_DEFAULT_PORT + obj.srvid - 1)
|
||||||
|
return obj.port
|
||||||
|
|
||||||
|
get_port.short_description = _('Server Port')
|
||||||
|
|
||||||
def get_booted( self, obj ):
|
def get_booted( self, obj ):
|
||||||
return obj.booted
|
return obj.booted
|
||||||
|
|
||||||
get_booted.short_description = _('Boot Server')
|
get_booted.short_description = _('Instance is running')
|
||||||
get_booted.boolean = True
|
get_booted.boolean = True
|
||||||
|
|
||||||
def get_users_regged( self, obj ):
|
def get_users_regged( self, obj ):
|
||||||
@@ -88,14 +122,17 @@ class MumbleUserAdmin(admin.ModelAdmin):
|
|||||||
search_fields = [ 'owner__username', 'name' ];
|
search_fields = [ 'owner__username', 'name' ];
|
||||||
ordering = [ 'owner__username' ];
|
ordering = [ 'owner__username' ];
|
||||||
|
|
||||||
#form = MumbleUserAdminForm
|
form = MumbleUserAdminForm
|
||||||
|
|
||||||
def get_acl_admin( self, obj ):
|
def get_acl_admin( self, obj ):
|
||||||
|
if obj.server.booted:
|
||||||
return obj.aclAdmin
|
return obj.aclAdmin
|
||||||
|
return None
|
||||||
|
|
||||||
get_acl_admin.short_description = _('Admin on root channel')
|
get_acl_admin.short_description = _('Admin on root channel')
|
||||||
get_acl_admin.boolean = True
|
get_acl_admin.boolean = True
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register( MumbleServer, MumbleServerAdmin );
|
||||||
admin.site.register( Mumble, MumbleAdmin );
|
admin.site.register( Mumble, MumbleAdmin );
|
||||||
admin.site.register( MumbleUser, MumbleUserAdmin );
|
admin.site.register( MumbleUser, MumbleUserAdmin );
|
||||||
|
|||||||
18
mumble/conversionsql/mysql/01-schema-prepare-mumble.sql
Normal file
18
mumble/conversionsql/mysql/01-schema-prepare-mumble.sql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
BEGIN;
|
||||||
|
-- Model: Mumble
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
ADD `server_id` integer FIRST;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
ADD `display` varchar(200) AFTER `name`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
MODIFY `port` integer NULL;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
ALTER TABLE mumble_mumble DROP KEY `addr`;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
CREATE INDEX `mumble_mumble_server_id_idx`
|
||||||
|
ON `mumble_mumble` (`server_id`);
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
INSERT INTO `mumble_mumbleserver` ( `dbus`, `secret` )
|
||||||
|
SELECT DISTINCT `dbus`, ''
|
||||||
|
FROM `mumble_mumble`;
|
||||||
6
mumble/conversionsql/mysql/12-data-mumble_mumble.sql
Normal file
6
mumble/conversionsql/mysql/12-data-mumble_mumble.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
UPDATE `mumble_mumble`
|
||||||
|
SET `server_id`=(
|
||||||
|
SELECT `id`
|
||||||
|
FROM `mumble_mumbleserver`
|
||||||
|
WHERE `mumble_mumbleserver`.`dbus` = `mumble_mumble`.`dbus`
|
||||||
|
);
|
||||||
29
mumble/conversionsql/mysql/21-schema-cleanup-mumble.sql
Normal file
29
mumble/conversionsql/mysql/21-schema-cleanup-mumble.sql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
-- Model: Mumble
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `dbus`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `url`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `motd`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `passwd`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `supw`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `users`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `bwidth`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `sslcrt`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `sslkey`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `obfsc`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `player`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `channel`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `defchan`;
|
||||||
|
ALTER TABLE `mumble_mumble`
|
||||||
|
DROP COLUMN `booted`;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- Model: MumbleUser
|
||||||
|
ALTER TABLE `mumble_mumbleuser`
|
||||||
|
DROP COLUMN `isAdmin`;
|
||||||
13
mumble/conversionsql/pgsql/01-schema-prepare-mumble.sql
Normal file
13
mumble/conversionsql/pgsql/01-schema-prepare-mumble.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Model: Mumble
|
||||||
|
BEGIN;
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
ADD "server_id" integer NULL REFERENCES "mumble_mumbleserver" ("id") DEFERRABLE INITIALLY DEFERRED;
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
ADD "display" varchar(200);
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
ALTER "port" DROP NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX "mumble_mumble_server_id" ON "mumble_mumble" ("server_id");
|
||||||
|
|
||||||
|
ALTER TABLE "mumble_mumble" DROP CONSTRAINT "mumble_mumble_addr_key";
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
BEGIN;
|
||||||
|
INSERT INTO "mumble_mumbleserver" ( "dbus", "secret" )
|
||||||
|
SELECT DISTINCT "dbus", ''
|
||||||
|
FROM "mumble_mumble";
|
||||||
|
COMMIT;
|
||||||
8
mumble/conversionsql/pgsql/12-data-mumble_mumble.sql
Normal file
8
mumble/conversionsql/pgsql/12-data-mumble_mumble.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
BEGIN;
|
||||||
|
UPDATE "mumble_mumble"
|
||||||
|
SET "server_id"=(
|
||||||
|
SELECT "id"
|
||||||
|
FROM "mumble_mumbleserver"
|
||||||
|
WHERE "mumble_mumbleserver"."dbus" = "mumble_mumble"."dbus"
|
||||||
|
);
|
||||||
|
COMMIT;
|
||||||
34
mumble/conversionsql/pgsql/21-schema-cleanup-mumble.sql
Normal file
34
mumble/conversionsql/pgsql/21-schema-cleanup-mumble.sql
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
-- Model: Mumble
|
||||||
|
BEGIN;
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "dbus";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "url";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "motd";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "passwd";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "supw";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "users";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "bwidth";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "sslcrt";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "sslkey";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "obfsc";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "player";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "channel";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "defchan";
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
DROP COLUMN "booted";
|
||||||
|
|
||||||
|
ALTER TABLE "mumble_mumble"
|
||||||
|
ALTER COLUMN "server_id" SET NOT NULL;
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- Model: MumbleUser
|
||||||
|
BEGIN;
|
||||||
|
ALTER TABLE "mumble_mumbleuser"
|
||||||
|
DROP COLUMN "isAdmin";
|
||||||
|
COMMIT;
|
||||||
10
mumble/conversionsql/sqlite/01-schema-mumble.sql
Normal file
10
mumble/conversionsql/sqlite/01-schema-mumble.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE "mumble_mumble_new" (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY,
|
||||||
|
"server_id" integer NOT NULL REFERENCES "mumble_mumbleserver" ("id"),
|
||||||
|
"name" varchar(200) NOT NULL,
|
||||||
|
"srvid" integer NOT NULL,
|
||||||
|
"addr" varchar(200) NOT NULL,
|
||||||
|
"port" integer,
|
||||||
|
"display" varchar(200) NOT NULL,
|
||||||
|
UNIQUE ("server_id", "srvid")
|
||||||
|
);
|
||||||
10
mumble/conversionsql/sqlite/02-schema-mumbleuser.sql
Normal file
10
mumble/conversionsql/sqlite/02-schema-mumbleuser.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE "mumble_mumbleuser_new" (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY,
|
||||||
|
"mumbleid" integer NOT NULL,
|
||||||
|
"name" varchar(200) NOT NULL,
|
||||||
|
"password" varchar(200) NOT NULL,
|
||||||
|
"server_id" integer NOT NULL REFERENCES "mumble_mumble" ("id"),
|
||||||
|
"owner_id" integer REFERENCES "auth_user" ("id"),
|
||||||
|
UNIQUE ("server_id", "owner_id"),
|
||||||
|
UNIQUE ("server_id", "mumbleid")
|
||||||
|
);
|
||||||
3
mumble/conversionsql/sqlite/11-data-mumbleserver.sql
Normal file
3
mumble/conversionsql/sqlite/11-data-mumbleserver.sql
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
INSERT INTO "mumble_mumbleserver" ( "dbus", "secret" )
|
||||||
|
SELECT DISTINCT "dbus", ''
|
||||||
|
FROM "mumble_mumble";
|
||||||
11
mumble/conversionsql/sqlite/12-data-mumble.sql
Normal file
11
mumble/conversionsql/sqlite/12-data-mumble.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
INSERT INTO "mumble_mumble_new"
|
||||||
|
SELECT
|
||||||
|
"mumble_mumble"."id",
|
||||||
|
"mumble_mumbleserver"."id",
|
||||||
|
"mumble_mumble"."name",
|
||||||
|
"mumble_mumble"."srvid",
|
||||||
|
"mumble_mumble"."addr",
|
||||||
|
"mumble_mumble"."port",
|
||||||
|
''
|
||||||
|
FROM "mumble_mumble" INNER JOIN "mumble_mumbleserver"
|
||||||
|
WHERE "mumble_mumbleserver"."dbus" = "mumble_mumble"."dbus";
|
||||||
9
mumble/conversionsql/sqlite/13-data-mumbleuser.sql
Normal file
9
mumble/conversionsql/sqlite/13-data-mumbleuser.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
INSERT INTO "mumble_mumbleuser_new"
|
||||||
|
SELECT
|
||||||
|
"id",
|
||||||
|
"mumbleid",
|
||||||
|
"name",
|
||||||
|
"password",
|
||||||
|
"server_id",
|
||||||
|
"owner_id"
|
||||||
|
FROM "mumble_mumbleuser";
|
||||||
1
mumble/conversionsql/sqlite/21-remove-old-mumble.sql
Normal file
1
mumble/conversionsql/sqlite/21-remove-old-mumble.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE "mumble_mumble";
|
||||||
1
mumble/conversionsql/sqlite/22-rename-new-mumble.sql
Normal file
1
mumble/conversionsql/sqlite/22-rename-new-mumble.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "mumble_mumble_new" RENAME TO "mumble_mumble";
|
||||||
1
mumble/conversionsql/sqlite/23-remove-old-mumbleuser.sql
Normal file
1
mumble/conversionsql/sqlite/23-remove-old-mumbleuser.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE "mumble_mumbleuser";
|
||||||
1
mumble/conversionsql/sqlite/24-rename-new-mumbleuser.sql
Normal file
1
mumble/conversionsql/sqlite/24-rename-new-mumbleuser.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "mumble_mumbleuser_new" RENAME TO "mumble_mumbleuser";
|
||||||
1
mumble/evolutions/__init__.py
Normal file
1
mumble/evolutions/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SEQUENCE = ['mumble-db-update-1']
|
||||||
10
mumble/evolutions/mumble-db-update-1.py
Normal file
10
mumble/evolutions/mumble-db-update-1.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from django_evolution.mutations import *
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
MUTATIONS = [
|
||||||
|
AddField('Mumble', 'display', models.CharField, initial='', max_length=200),
|
||||||
|
AddField('Mumble', 'server', models.ForeignKey, initial='<<USER VALUE REQUIRED>>', related_model='mumble.MumbleServer'),
|
||||||
|
DeleteField('Mumble', 'dbus'),
|
||||||
|
ChangeField('Mumble', 'port', initial=None, null=True)
|
||||||
|
]
|
||||||
|
|
||||||
324
mumble/forms.py
Normal file
324
mumble/forms.py
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import re
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
|
from django.forms import Form, ModelForm
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from mumble.models import MumbleServer, Mumble, MumbleUser
|
||||||
|
|
||||||
|
|
||||||
|
class PropertyModelForm( ModelForm ):
|
||||||
|
""" ModelForm that gets/sets fields that are not within the model's
|
||||||
|
fields as model attributes. Necessary to get forms that manipulate
|
||||||
|
properties.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__( self, *args, **kwargs ):
|
||||||
|
ModelForm.__init__( self, *args, **kwargs );
|
||||||
|
|
||||||
|
if self.instance:
|
||||||
|
instfields = self.instance._meta.get_all_field_names()
|
||||||
|
for fldname in self.fields:
|
||||||
|
if fldname in instfields:
|
||||||
|
continue
|
||||||
|
self.fields[fldname].initial = getattr( self.instance, fldname )
|
||||||
|
prop = getattr( self.instance.__class__, fldname )
|
||||||
|
if prop.__doc__:
|
||||||
|
self.fields[fldname].label = _(prop.__doc__)
|
||||||
|
|
||||||
|
def save( self, commit=True ):
|
||||||
|
inst = ModelForm.save( self, commit=commit )
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
self.save_to_model( inst )
|
||||||
|
else:
|
||||||
|
# Update when the model has been saved.
|
||||||
|
from django.db.models import signals
|
||||||
|
self._update_inst = inst
|
||||||
|
signals.post_save.connect( self.save_listener, sender=inst.__class__ )
|
||||||
|
|
||||||
|
return inst
|
||||||
|
|
||||||
|
def save_listener( self, **kwargs ):
|
||||||
|
if kwargs['instance'] is self._update_inst:
|
||||||
|
self.save_to_model( self._update_inst )
|
||||||
|
|
||||||
|
def save_to_model( self, inst ):
|
||||||
|
instfields = inst._meta.get_all_field_names()
|
||||||
|
|
||||||
|
for fldname in self.fields:
|
||||||
|
if fldname not in instfields:
|
||||||
|
setattr( inst, fldname, self.cleaned_data[fldname] )
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleForm( PropertyModelForm ):
|
||||||
|
""" The Mumble Server admin form that allows to configure settings which do
|
||||||
|
not necessarily have to be reserved to the server hoster.
|
||||||
|
|
||||||
|
Server hosters are expected to use the Django admin application instead,
|
||||||
|
where everything can be configured freely.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = forms.CharField( required=False )
|
||||||
|
motd = forms.CharField( required=False, widget=forms.Textarea )
|
||||||
|
passwd = forms.CharField( required=False, help_text=_(
|
||||||
|
"Password required to join. Leave empty for public servers.") )
|
||||||
|
supw = forms.CharField( required=False )
|
||||||
|
obfsc = forms.BooleanField( required=False, help_text=_(
|
||||||
|
"If on, IP adresses of the clients are not logged.") )
|
||||||
|
player = forms.CharField( required=False )
|
||||||
|
channel = forms.CharField( required=False )
|
||||||
|
defchan = forms.TypedChoiceField( choices=(), coerce=int, required=False )
|
||||||
|
timeout = forms.IntegerField( required=False )
|
||||||
|
certreq = forms.BooleanField( required=False )
|
||||||
|
textlen = forms.IntegerField( required=False )
|
||||||
|
html = forms.BooleanField( required=False )
|
||||||
|
|
||||||
|
def __init__( self, *args, **kwargs ):
|
||||||
|
PropertyModelForm.__init__( self, *args, **kwargs )
|
||||||
|
|
||||||
|
# Populate the `default channel' field's choices
|
||||||
|
choices = [ ('', '----------') ]
|
||||||
|
|
||||||
|
if self.instance and self.instance.srvid is not None:
|
||||||
|
if self.instance.booted:
|
||||||
|
def add_item( item, level ):
|
||||||
|
if item.is_server or item.is_channel:
|
||||||
|
choices.append( ( item.chanid, ( "-"*level + " " + item.name ) ) )
|
||||||
|
|
||||||
|
self.instance.rootchan.visit(add_item)
|
||||||
|
else:
|
||||||
|
current = self.instance.defchan
|
||||||
|
if current is not None:
|
||||||
|
choices.append( ( current, "Current value: %d" % current ) )
|
||||||
|
self.fields['defchan'].choices = choices
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Mumble;
|
||||||
|
fields = ['name'];
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleAdminForm( MumbleForm ):
|
||||||
|
""" A Mumble Server admin form intended to be used by the server hoster. """
|
||||||
|
|
||||||
|
users = forms.IntegerField( required=False )
|
||||||
|
bwidth = forms.IntegerField( required=False )
|
||||||
|
sslcrt = forms.CharField( required=False, widget=forms.Textarea )
|
||||||
|
sslkey = forms.CharField( required=False, widget=forms.Textarea )
|
||||||
|
booted = forms.BooleanField( required=False )
|
||||||
|
autoboot = forms.BooleanField( required=False )
|
||||||
|
bonjour = forms.BooleanField( required=False )
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
fields = None
|
||||||
|
exclude = None
|
||||||
|
|
||||||
|
def clean_port( self ):
|
||||||
|
""" Check if the port number is valid. """
|
||||||
|
|
||||||
|
port = self.cleaned_data['port'];
|
||||||
|
|
||||||
|
if port is not None and port != '':
|
||||||
|
if port < 1 or port >= 2**16:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
_("Port number %(portno)d is not within the allowed range %(minrange)d - %(maxrange)d") % {
|
||||||
|
'portno': port,
|
||||||
|
'minrange': 1,
|
||||||
|
'maxrange': 2**16,
|
||||||
|
});
|
||||||
|
return port;
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleServerForm( ModelForm ):
|
||||||
|
defaultconf = forms.CharField( label=_("Default config"), required=False, widget=forms.Textarea )
|
||||||
|
|
||||||
|
def __init__( self, *args, **kwargs ):
|
||||||
|
ModelForm.__init__( self, *args, **kwargs )
|
||||||
|
|
||||||
|
if self.instance and self.instance.id:
|
||||||
|
if self.instance.online:
|
||||||
|
confstr = ""
|
||||||
|
conf = self.instance.defaultconf
|
||||||
|
for field in conf:
|
||||||
|
confstr += "%s: %s\n" % ( field, conf[field] )
|
||||||
|
self.fields["defaultconf"].initial = confstr
|
||||||
|
else:
|
||||||
|
self.fields["defaultconf"].initial = _("This server is currently offline.")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MumbleServer
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleUserForm( ModelForm ):
|
||||||
|
""" The user registration form used to register an account. """
|
||||||
|
|
||||||
|
password = forms.CharField( widget=forms.PasswordInput, required=False )
|
||||||
|
|
||||||
|
def __init__( self, *args, **kwargs ):
|
||||||
|
ModelForm.__init__( self, *args, **kwargs );
|
||||||
|
self.server = None;
|
||||||
|
|
||||||
|
def clean_name( self ):
|
||||||
|
""" Check if the desired name is forbidden or taken. """
|
||||||
|
|
||||||
|
name = self.cleaned_data['name'];
|
||||||
|
|
||||||
|
if self.server is None:
|
||||||
|
raise AttributeError( "You need to set the form's server attribute to the server instance "
|
||||||
|
"for validation to work." );
|
||||||
|
|
||||||
|
if self.server.player and re.compile( self.server.player ).match( name ) is None:
|
||||||
|
raise forms.ValidationError( _( "That name is forbidden by the server." ) );
|
||||||
|
|
||||||
|
if not self.instance.id and len( self.server.ctl.getRegisteredPlayers( self.server.srvid, name ) ) > 0:
|
||||||
|
raise forms.ValidationError( _( "Another player already registered that name." ) );
|
||||||
|
|
||||||
|
return name;
|
||||||
|
|
||||||
|
def clean_password( self ):
|
||||||
|
""" Verify a password has been given. """
|
||||||
|
passwd = self.cleaned_data['password'];
|
||||||
|
if not passwd and ( not self.instance or self.instance.mumbleid == -1 ):
|
||||||
|
raise forms.ValidationError( _( "Cannot register player without a password!" ) );
|
||||||
|
return passwd;
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MumbleUser;
|
||||||
|
fields = ( 'name', 'password' );
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleUserPasswordForm( MumbleUserForm ):
|
||||||
|
""" The user registration form used to register an account on a private server in protected mode. """
|
||||||
|
|
||||||
|
serverpw = forms.CharField(
|
||||||
|
label=_('Server Password'),
|
||||||
|
help_text=_('This server is private and protected mode is active. Please enter the server password.'),
|
||||||
|
widget=forms.PasswordInput(render_value=False)
|
||||||
|
);
|
||||||
|
|
||||||
|
def clean_serverpw( self ):
|
||||||
|
""" Validate the password """
|
||||||
|
serverpw = self.cleaned_data['serverpw'];
|
||||||
|
if self.server.passwd != serverpw:
|
||||||
|
raise forms.ValidationError( _( "The password you entered is incorrect." ) );
|
||||||
|
return serverpw;
|
||||||
|
|
||||||
|
def clean( self ):
|
||||||
|
""" prevent save() from trying to store the password in the Model instance. """
|
||||||
|
# clean() will be called after clean_serverpw(), so it has already been validated here.
|
||||||
|
if 'serverpw' in self.cleaned_data:
|
||||||
|
del( self.cleaned_data['serverpw'] );
|
||||||
|
return self.cleaned_data;
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleUserLinkForm( MumbleUserForm ):
|
||||||
|
""" Special registration form to either register or link an account. """
|
||||||
|
|
||||||
|
linkacc = forms.BooleanField(
|
||||||
|
label=_('Link account'),
|
||||||
|
help_text=_('The account already exists and belongs to me, just link it instead of creating.'),
|
||||||
|
required=False,
|
||||||
|
);
|
||||||
|
|
||||||
|
def __init__( self, *args, **kwargs ):
|
||||||
|
MumbleUserForm.__init__( self, *args, **kwargs );
|
||||||
|
self.mumbleid = None;
|
||||||
|
|
||||||
|
def clean_name( self ):
|
||||||
|
""" Check if the target account exists in Murmur. """
|
||||||
|
if 'linkacc' not in self.data:
|
||||||
|
return MumbleUserForm.clean_name( self );
|
||||||
|
|
||||||
|
# Check if user exists
|
||||||
|
name = self.cleaned_data['name'];
|
||||||
|
|
||||||
|
if len( self.server.ctl.getRegisteredPlayers( self.server.srvid, name ) ) != 1:
|
||||||
|
raise forms.ValidationError( _( "No such user found." ) );
|
||||||
|
|
||||||
|
return name;
|
||||||
|
|
||||||
|
def clean_password( self ):
|
||||||
|
""" Verify that the password is correct. """
|
||||||
|
if 'linkacc' not in self.data:
|
||||||
|
return MumbleUserForm.clean_password( self );
|
||||||
|
|
||||||
|
if 'name' not in self.cleaned_data:
|
||||||
|
# keep clean() from trying to find a user that CAN'T exist
|
||||||
|
self.mumbleid = -10;
|
||||||
|
return '';
|
||||||
|
|
||||||
|
# Validate password with Murmur
|
||||||
|
passwd = self.cleaned_data['password'];
|
||||||
|
|
||||||
|
self.mumbleid = self.server.ctl.verifyPassword( self.server.srvid, self.cleaned_data['name'], passwd )
|
||||||
|
if self.mumbleid <= 0:
|
||||||
|
raise forms.ValidationError( _( "The password you entered is incorrect." ) );
|
||||||
|
|
||||||
|
return passwd;
|
||||||
|
|
||||||
|
def clean( self ):
|
||||||
|
""" Create the MumbleUser instance to save in. """
|
||||||
|
if 'linkacc' not in self.data or self.mumbleid <= 0:
|
||||||
|
return self.cleaned_data;
|
||||||
|
|
||||||
|
try:
|
||||||
|
m_user = MumbleUser.objects.get( server=self.server, mumbleid=self.mumbleid );
|
||||||
|
except MumbleUser.DoesNotExist:
|
||||||
|
m_user = MumbleUser( server=self.server, name=self.cleaned_data['name'], mumbleid=self.mumbleid );
|
||||||
|
m_user.save( dontConfigureMurmur=True );
|
||||||
|
else:
|
||||||
|
if m_user.owner is not None:
|
||||||
|
raise forms.ValidationError( _( "That account belongs to someone else." ) );
|
||||||
|
|
||||||
|
if m_user.getAdmin() and not settings.ALLOW_ACCOUNT_LINKING_ADMINS:
|
||||||
|
raise forms.ValidationError( _( "Linking Admin accounts is not allowed." ) );
|
||||||
|
self.instance = m_user;
|
||||||
|
|
||||||
|
return self.cleaned_data;
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleUserAdminForm( PropertyModelForm ):
|
||||||
|
aclAdmin = forms.BooleanField( required=False );
|
||||||
|
password = forms.CharField( widget=forms.PasswordInput, required=False )
|
||||||
|
|
||||||
|
def clean_password( self ):
|
||||||
|
""" Verify a password has been given. """
|
||||||
|
passwd = self.cleaned_data['password'];
|
||||||
|
if not passwd and ( not self.instance or self.instance.mumbleid == -1 ):
|
||||||
|
raise forms.ValidationError( _( "Cannot register player without a password!" ) );
|
||||||
|
return passwd;
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Mumble;
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleKickForm( Form ):
|
||||||
|
session = forms.IntegerField();
|
||||||
|
ban = forms.BooleanField( required=False );
|
||||||
|
reason = forms.CharField( required=False );
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleTextureForm( Form ):
|
||||||
|
""" The form used to upload a new image to be set as texture. """
|
||||||
|
texturefile = forms.ImageField( label=_("User Texture") );
|
||||||
|
|
||||||
|
|
||||||
BIN
mumble/locale/_outdated/hr/LC_MESSAGES/django.mo
Normal file
BIN
mumble/locale/_outdated/hr/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
580
mumble/locale/_outdated/hr/LC_MESSAGES/django.po
Normal file
580
mumble/locale/_outdated/hr/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,580 @@
|
|||||||
|
# Croatian translation file for Mumble-Django.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009, Vid "Night" Marić <vid_maric@yahoo.com>
|
||||||
|
#
|
||||||
|
# Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This package is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Mumble-Django v0.8\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2010-03-11 16:43+0100\n"
|
||||||
|
"PO-Revision-Date: 2010-01-11 14:11\n"
|
||||||
|
"Last-Translator: <de@de.de>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Translated-Using: django-rosetta 0.5.1\n"
|
||||||
|
|
||||||
|
#: admin.py:34 admin.py:51
|
||||||
|
msgid "Master is running"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: admin.py:59 models.py:162 templates/mumble/mumble.html:28
|
||||||
|
msgid "Server Address"
|
||||||
|
msgstr "Adresa servera"
|
||||||
|
|
||||||
|
#: admin.py:66 models.py:165
|
||||||
|
msgid "Server Port"
|
||||||
|
msgstr "Port"
|
||||||
|
|
||||||
|
#: admin.py:71
|
||||||
|
msgid "Instance is running"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: admin.py:81
|
||||||
|
msgid "Registered users"
|
||||||
|
msgstr "Registrirani korisnici"
|
||||||
|
|
||||||
|
#: admin.py:91
|
||||||
|
msgid "Online users"
|
||||||
|
msgstr "Korisnici na serveru"
|
||||||
|
|
||||||
|
#: admin.py:101
|
||||||
|
msgid "Channel count"
|
||||||
|
msgstr "Broj kanala"
|
||||||
|
|
||||||
|
#: admin.py:108
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: admin.py:110
|
||||||
|
msgid "No"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: admin.py:114
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Javni server"
|
||||||
|
|
||||||
|
#: admin.py:132 models.py:626 templates/mumble/mumble.html:223
|
||||||
|
msgid "Admin on root channel"
|
||||||
|
msgstr "Administrator u glavnoj sobi"
|
||||||
|
|
||||||
|
#: forms.py:83
|
||||||
|
msgid "Password required to join. Leave empty for public servers."
|
||||||
|
msgstr ""
|
||||||
|
"Lozinka za pristup serveru. Ostaviti prazno ako se spajate na javni server."
|
||||||
|
|
||||||
|
#: forms.py:86
|
||||||
|
msgid "If on, IP adresses of the clients are not logged."
|
||||||
|
msgstr "Ako je uključeno, IP adrese klijenata se neće bilježiti."
|
||||||
|
|
||||||
|
#: forms.py:142
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Port number %(portno)d is not within the allowed range %(minrange)d - %"
|
||||||
|
"(maxrange)d"
|
||||||
|
msgstr "Port %(portno)d nije u dozvoljenom opsegu %(minrange)d - %(maxrange)d"
|
||||||
|
|
||||||
|
#: forms.py:165 templates/mumble/offline.html:12
|
||||||
|
msgid "This server is currently offline."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:190
|
||||||
|
msgid "That name is forbidden by the server."
|
||||||
|
msgstr "Ime je zabranjeno na serveru."
|
||||||
|
|
||||||
|
#: forms.py:193 models.py:582
|
||||||
|
msgid "Another player already registered that name."
|
||||||
|
msgstr "Ovo ime je zauzeto, probajte neko drugo."
|
||||||
|
|
||||||
|
#: forms.py:201 forms.py:307 models.py:584
|
||||||
|
msgid "Cannot register player without a password!"
|
||||||
|
msgstr "Odaberite lozinku i probajte ponovno!"
|
||||||
|
|
||||||
|
#: forms.py:213 models.py:179
|
||||||
|
msgid "Server Password"
|
||||||
|
msgstr "Lozinka servera"
|
||||||
|
|
||||||
|
#: forms.py:214
|
||||||
|
msgid ""
|
||||||
|
"This server is private and protected mode is active. Please enter the server "
|
||||||
|
"password."
|
||||||
|
msgstr ""
|
||||||
|
"Ovo je privatan server i potrebna vam je lozinka za pristup. Molimo vas "
|
||||||
|
"unesite lozinku."
|
||||||
|
|
||||||
|
#: forms.py:222 forms.py:274
|
||||||
|
msgid "The password you entered is incorrect."
|
||||||
|
msgstr "Lozinka koju ste unijeli je neispravna."
|
||||||
|
|
||||||
|
#: forms.py:237
|
||||||
|
msgid "Link account"
|
||||||
|
msgstr "Povezani račun"
|
||||||
|
|
||||||
|
#: forms.py:238
|
||||||
|
msgid ""
|
||||||
|
"The account already exists and belongs to me, just link it instead of "
|
||||||
|
"creating."
|
||||||
|
msgstr "Ovaj račun već postoji. Povežite ga umjesto da radite novi račun."
|
||||||
|
|
||||||
|
#: forms.py:255
|
||||||
|
msgid "No such user found."
|
||||||
|
msgstr "Korisnik nije pronađen."
|
||||||
|
|
||||||
|
#: forms.py:290
|
||||||
|
msgid "That account belongs to someone else."
|
||||||
|
msgstr "Ovaj račun pripada drugome korisniku."
|
||||||
|
|
||||||
|
#: forms.py:293
|
||||||
|
msgid "Linking Admin accounts is not allowed."
|
||||||
|
msgstr "Povezivanje administratorskih računa nije dozvoljeno."
|
||||||
|
|
||||||
|
#: forms.py:322 templates/mumble/mumble.html:65
|
||||||
|
#: templates/mumble/mumble.html.py:148 templates/mumble/mumble.html:278
|
||||||
|
msgid "User Texture"
|
||||||
|
msgstr "Korisnička slika"
|
||||||
|
|
||||||
|
#: models.py:63
|
||||||
|
msgid "DBus or ICE base"
|
||||||
|
msgstr "DBus ili ICE"
|
||||||
|
|
||||||
|
#: models.py:64
|
||||||
|
msgid ""
|
||||||
|
"Examples: 'net.sourceforge.mumble.murmur' for DBus or 'Meta:tcp -h 127.0.0.1 "
|
||||||
|
"-p 6502' for Ice."
|
||||||
|
msgstr ""
|
||||||
|
"Primjeri: 'net.sourceforge.mumble.murmur' za DBus ili 'Meta:tcp -h 127.0.0.1 "
|
||||||
|
"-p 6502' za Ice."
|
||||||
|
|
||||||
|
#: models.py:65
|
||||||
|
msgid "Ice Secret"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:68
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Mumble Server"
|
||||||
|
msgstr "ID korisnika na Mumbleu"
|
||||||
|
|
||||||
|
#: models.py:69
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Mumble Servers"
|
||||||
|
msgstr "ID korisnika na Mumbleu"
|
||||||
|
|
||||||
|
#: models.py:160
|
||||||
|
msgid "Server Name"
|
||||||
|
msgstr "Ime servera"
|
||||||
|
|
||||||
|
#: models.py:161
|
||||||
|
msgid "Server ID"
|
||||||
|
msgstr "ID servera"
|
||||||
|
|
||||||
|
#: models.py:163
|
||||||
|
msgid ""
|
||||||
|
"Hostname or IP address to bind to. You should use a hostname here, because "
|
||||||
|
"it will appear on the global server list."
|
||||||
|
msgstr ""
|
||||||
|
"Ime poslužitelja (hostname) ili IP adresa adresa za spajanje. Koristite ime "
|
||||||
|
"poslužitelja ovdje zato što će se pojaviti na globalnoj listi servera."
|
||||||
|
|
||||||
|
#: models.py:166
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Port number to bind to. Leave empty to auto assign one."
|
||||||
|
msgstr "Port za spajanje. Koristite -1 za automatsko dodjeljivanje porta."
|
||||||
|
|
||||||
|
#: models.py:167
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Server Display Address"
|
||||||
|
msgstr "Adresa servera"
|
||||||
|
|
||||||
|
#: models.py:168
|
||||||
|
msgid ""
|
||||||
|
"This field is only relevant if you are located behind a NAT, and names the "
|
||||||
|
"Hostname or IP address to use in the Channel Viewer and for the global "
|
||||||
|
"server list registration. If not given, the addr and port fields are used. "
|
||||||
|
"If display and bind ports are equal, you can omit it here."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:174
|
||||||
|
msgid "Superuser Password"
|
||||||
|
msgstr "Lozinka Superusera (administrator)"
|
||||||
|
|
||||||
|
#: models.py:177
|
||||||
|
msgid "Website URL"
|
||||||
|
msgstr "URL internet stranice"
|
||||||
|
|
||||||
|
#: models.py:178
|
||||||
|
msgid "Welcome Message"
|
||||||
|
msgstr "Poruka dobrodošlice"
|
||||||
|
|
||||||
|
#: models.py:180
|
||||||
|
msgid "Max. Users"
|
||||||
|
msgstr "Maksimalan broj korisnika"
|
||||||
|
|
||||||
|
#: models.py:181
|
||||||
|
msgid "Bandwidth [Bps]"
|
||||||
|
msgstr "Promet [Bps]"
|
||||||
|
|
||||||
|
#: models.py:182
|
||||||
|
msgid "SSL Certificate"
|
||||||
|
msgstr "SSL certifikat"
|
||||||
|
|
||||||
|
#: models.py:183
|
||||||
|
msgid "SSL Key"
|
||||||
|
msgstr "SSL ključ"
|
||||||
|
|
||||||
|
#: models.py:184
|
||||||
|
msgid "Player name regex"
|
||||||
|
msgstr "Dozvoljeni znakovi u nazivu korisnika"
|
||||||
|
|
||||||
|
#: models.py:185
|
||||||
|
msgid "Channel name regex"
|
||||||
|
msgstr "Dozvoljeni znakovi u nazivu kanala"
|
||||||
|
|
||||||
|
#: models.py:186
|
||||||
|
msgid "Default channel"
|
||||||
|
msgstr "Početni kanal"
|
||||||
|
|
||||||
|
#: models.py:187
|
||||||
|
msgid "Timeout"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:189
|
||||||
|
msgid "IP Obfuscation"
|
||||||
|
msgstr "Bilježi IP adrese korisnika"
|
||||||
|
|
||||||
|
#: models.py:190
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Require Certificate"
|
||||||
|
msgstr "SSL certifikat"
|
||||||
|
|
||||||
|
#: models.py:191
|
||||||
|
msgid "Maximum length of text messages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:192
|
||||||
|
msgid "Allow HTML to be used in messages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:193
|
||||||
|
msgid "Publish this server via Bonjour"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:194
|
||||||
|
msgid "Boot Server when Murmur starts"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:212 models.py:213
|
||||||
|
msgid "Boot Server"
|
||||||
|
msgstr "Pokreni server"
|
||||||
|
|
||||||
|
#: models.py:217 models.py:545
|
||||||
|
msgid "Server instance"
|
||||||
|
msgstr "Instanca servera"
|
||||||
|
|
||||||
|
#: models.py:218
|
||||||
|
msgid "Server instances"
|
||||||
|
msgstr "Instance servera"
|
||||||
|
|
||||||
|
#: models.py:505 models.py:685
|
||||||
|
msgid "This field must not be updated once the record has been saved."
|
||||||
|
msgstr "Ovo polje ne smije biti ažurirano nakon što je zapis spremljen."
|
||||||
|
|
||||||
|
#: models.py:542
|
||||||
|
msgid "Mumble player_id"
|
||||||
|
msgstr "ID korisnika na Mumbleu"
|
||||||
|
|
||||||
|
#: models.py:543
|
||||||
|
msgid "User name and Login"
|
||||||
|
msgstr "Korisničko ime"
|
||||||
|
|
||||||
|
#: models.py:544
|
||||||
|
msgid "Login password"
|
||||||
|
msgstr "Lozinka"
|
||||||
|
|
||||||
|
#: models.py:546 templates/mumble/mumble.html:293
|
||||||
|
msgid "Account owner"
|
||||||
|
msgstr "Vlasnik računa"
|
||||||
|
|
||||||
|
#: models.py:548
|
||||||
|
#, fuzzy
|
||||||
|
msgid "The user's comment."
|
||||||
|
msgstr "Korisnički račun"
|
||||||
|
|
||||||
|
#: models.py:549
|
||||||
|
msgid "The user's hash."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:553
|
||||||
|
msgid "User account"
|
||||||
|
msgstr "Korisnički račun"
|
||||||
|
|
||||||
|
#: models.py:554
|
||||||
|
msgid "User accounts"
|
||||||
|
msgstr "Korisnički računi"
|
||||||
|
|
||||||
|
#: models.py:561
|
||||||
|
#, python-format
|
||||||
|
msgid "Mumble user %(mu)s on %(srv)s owned by Django user %(du)s"
|
||||||
|
msgstr "Django korisniku %(du)s pripada Mumble račun %(mu)s na serveru %(srv)s"
|
||||||
|
|
||||||
|
#: templates/mumble/list.html:20
|
||||||
|
msgid "No server instances have been configured yet."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:16
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <b>Hint:</b><br />\n"
|
||||||
|
" This area is used to display additional information for each channel "
|
||||||
|
"and player, but requires JavaScript to be\n"
|
||||||
|
" displayed correctly. You will not see the detail pages, but you can "
|
||||||
|
"use all links and forms\n"
|
||||||
|
" that are displayed.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <b>Savjet:</b><br />\n"
|
||||||
|
" Ovdje se prikazuju dodatne informacije za svaki kanali svakog igrača "
|
||||||
|
"te zahtjeva JavaScript kako bi\n"
|
||||||
|
" se informacije pravilno prikazale. Nećete vidjeti stranicu s "
|
||||||
|
"detaljima, ali možete koristiti sve linkove i forme.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:31
|
||||||
|
msgid "Website"
|
||||||
|
msgstr "Internet stranica"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:33
|
||||||
|
msgid "Server version"
|
||||||
|
msgstr "Verzija servera"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:34
|
||||||
|
msgid "Minimal view"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:37
|
||||||
|
msgid "Welcome message"
|
||||||
|
msgstr "Poruka dobrodošlice"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:43
|
||||||
|
msgid "Server registration"
|
||||||
|
msgstr "Registracija servera"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:46
|
||||||
|
msgid "You are registered on this server"
|
||||||
|
msgstr "Registrirani ste na ovom serveru"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:48
|
||||||
|
msgid "You do not have an account on this server"
|
||||||
|
msgstr "Nemate račun na ovom serveru"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:57
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <p>You need to be <a href=\"%(login_url)s\">logged in</a> to be able "
|
||||||
|
"to register an account on this Mumble server.</p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <p>Morate biti <a href=\"%(login_url)s\">prijavljeni (ulogirani)</a> "
|
||||||
|
"kako bi ste napravili račun na ovom Mumble serveru.</p>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:67
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Sorry, due to a bug in Murmur 1.2.2, displaying and setting the "
|
||||||
|
"Texture is disabled.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:72
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" You can upload an image that you would like to use as your user "
|
||||||
|
"texture here.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" Možete postaviti sliku za koju bi htjeli da zamjeni vašekorisničko "
|
||||||
|
"ime u Mumble transparentu (overlay).\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:77
|
||||||
|
msgid "Your current texture is"
|
||||||
|
msgstr "Vaša trenutačna slika je"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:80
|
||||||
|
msgid "You don't currently have a texture set"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:84
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Hint: The texture image <b>needs</b> to be 600x60 in size. If "
|
||||||
|
"you upload an image with\n"
|
||||||
|
" a different size, it will be resized accordingly.<br />\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" Savjet: Slika <b>mora</b> biti veličine 600x60. Ako odaberete "
|
||||||
|
"sliku drugačije veličine, veličina će biti promjenjena u 600x60.<br />\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:103
|
||||||
|
msgid "Server administration"
|
||||||
|
msgstr "Administracija servera"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:117
|
||||||
|
msgid "Player"
|
||||||
|
msgstr "Korisnik"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:119
|
||||||
|
msgid "Online since"
|
||||||
|
msgstr "Na serveru od"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:120 templates/mumble/player.html:9
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "Registriran korisnik"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:121 templates/mumble/mumble.html.py:136
|
||||||
|
msgid "Admin"
|
||||||
|
msgstr "Administrator"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:122 templates/mumble/player.html:12
|
||||||
|
msgid "Muted"
|
||||||
|
msgstr "Utišan mikrofon"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:123 templates/mumble/player.html:18
|
||||||
|
msgid "Deafened"
|
||||||
|
msgstr "Utišani zvučnici / slušalice"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:124 templates/mumble/player.html:21
|
||||||
|
msgid "Muted by self"
|
||||||
|
msgstr "Samo-utišan mikrofon"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:125 templates/mumble/player.html:24
|
||||||
|
msgid "Deafened by self"
|
||||||
|
msgstr "Samo-utišani zvučnici / slušalice"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:127
|
||||||
|
#, fuzzy
|
||||||
|
msgid "IP Address"
|
||||||
|
msgstr "Adresa servera"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:131
|
||||||
|
msgid "User"
|
||||||
|
msgstr "Korisnik"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:134
|
||||||
|
msgid "Full Name"
|
||||||
|
msgstr "Ime i prezime"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:137
|
||||||
|
msgid "Sign-up date"
|
||||||
|
msgstr "Datum registracije"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:142
|
||||||
|
#, fuzzy
|
||||||
|
msgid "User Comment"
|
||||||
|
msgstr "Korisnički račun"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:154 templates/mumble/mumble.html.py:168
|
||||||
|
msgid "Kick user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:160
|
||||||
|
msgid "Reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:165
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Ban user"
|
||||||
|
msgstr "Korisnici na serveru"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:175
|
||||||
|
msgid "Channel"
|
||||||
|
msgstr "Soba"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:177
|
||||||
|
msgid "Channel ID"
|
||||||
|
msgstr "ID kanala"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:179
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "Spoji se"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:182
|
||||||
|
msgid "Channel description"
|
||||||
|
msgstr "Opis kanala"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:229
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:265
|
||||||
|
msgid "Server Info"
|
||||||
|
msgstr "Informacije o serveru"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:266
|
||||||
|
msgid "Registration"
|
||||||
|
msgstr "Registracija"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:275
|
||||||
|
msgid "Administration"
|
||||||
|
msgstr "Administracija"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:282
|
||||||
|
msgid "User List"
|
||||||
|
msgstr "Lista korisnika"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:286
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:306
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Change password"
|
||||||
|
msgstr "Lozinka"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:319
|
||||||
|
msgid "Add"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:331
|
||||||
|
msgid "Save"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:357
|
||||||
|
msgid "Resync with Murmur"
|
||||||
|
msgstr "Resinkroniziraj s Murmurom"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:15
|
||||||
|
msgid "Suppressed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:27
|
||||||
|
msgid "has a User Comment set"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "Enter the ID of the default channel here. The Channel viewer displays the "
|
||||||
|
#~ "ID to server admins on the channel detail page."
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Unesite ID početnog kanala. Prikaz kanala pokazuje ID administratorima "
|
||||||
|
#~ "servera na stranici s detaljima kanala."
|
||||||
|
|
||||||
|
#~ msgid "The admin group was not found in the ACL's groups list!"
|
||||||
|
#~ msgstr "Grupa administratora nije pronađena u ACL listi grupa!"
|
||||||
BIN
mumble/locale/_outdated/pl/LC_MESSAGES/django.mo
Normal file
BIN
mumble/locale/_outdated/pl/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
574
mumble/locale/_outdated/pl/LC_MESSAGES/django.po
Normal file
574
mumble/locale/_outdated/pl/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,574 @@
|
|||||||
|
# Polskie tłumaczenia dla Mumble-Django.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009, HuNt3r <admin@serv4u.pl>
|
||||||
|
#
|
||||||
|
# Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This package is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Mumble-Django v0.8\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2010-03-11 16:43+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: HuNt3r <admin@serv4u.pl>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
#: admin.py:34 admin.py:51
|
||||||
|
msgid "Master is running"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: admin.py:59 models.py:162 templates/mumble/mumble.html:28
|
||||||
|
msgid "Server Address"
|
||||||
|
msgstr "Adres serwera"
|
||||||
|
|
||||||
|
#: admin.py:66 models.py:165
|
||||||
|
msgid "Server Port"
|
||||||
|
msgstr "Port serwera"
|
||||||
|
|
||||||
|
#: admin.py:71
|
||||||
|
msgid "Instance is running"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: admin.py:81
|
||||||
|
msgid "Registered users"
|
||||||
|
msgstr "Zarejestrowani użytkownicy"
|
||||||
|
|
||||||
|
#: admin.py:91
|
||||||
|
msgid "Online users"
|
||||||
|
msgstr "Użytkownicy online"
|
||||||
|
|
||||||
|
#: admin.py:101
|
||||||
|
msgid "Channel count"
|
||||||
|
msgstr "Kanały"
|
||||||
|
|
||||||
|
#: admin.py:108
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: admin.py:110
|
||||||
|
msgid "No"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: admin.py:114
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Publiczny"
|
||||||
|
|
||||||
|
#: admin.py:132 models.py:626 templates/mumble/mumble.html:223
|
||||||
|
msgid "Admin on root channel"
|
||||||
|
msgstr "Admin na kanale głównym"
|
||||||
|
|
||||||
|
#: forms.py:83
|
||||||
|
msgid "Password required to join. Leave empty for public servers."
|
||||||
|
msgstr "Podaj hasło, lub pozostaw puste pole, aby serwer był publiczny. "
|
||||||
|
|
||||||
|
#: forms.py:86
|
||||||
|
msgid "If on, IP adresses of the clients are not logged."
|
||||||
|
msgstr "Gdy zaznaczysz, adresy IP użytkowników nie będą logowane."
|
||||||
|
|
||||||
|
#: forms.py:142
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Port number %(portno)d is not within the allowed range %(minrange)d - %"
|
||||||
|
"(maxrange)d"
|
||||||
|
msgstr "Numer portu %(portno)d jest z poza zakresu %(minrange)d - %(maxrange)d"
|
||||||
|
|
||||||
|
#: forms.py:165 templates/mumble/offline.html:12
|
||||||
|
msgid "This server is currently offline."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:190
|
||||||
|
msgid "That name is forbidden by the server."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:193 models.py:582
|
||||||
|
msgid "Another player already registered that name."
|
||||||
|
msgstr "Ta nazwa użytkownika jest już zajęta."
|
||||||
|
|
||||||
|
#: forms.py:201 forms.py:307 models.py:584
|
||||||
|
msgid "Cannot register player without a password!"
|
||||||
|
msgstr "Musisz podać hasło!"
|
||||||
|
|
||||||
|
#: forms.py:213 models.py:179
|
||||||
|
msgid "Server Password"
|
||||||
|
msgstr "Hasło serwera"
|
||||||
|
|
||||||
|
#: forms.py:214
|
||||||
|
msgid ""
|
||||||
|
"This server is private and protected mode is active. Please enter the server "
|
||||||
|
"password."
|
||||||
|
msgstr "Ten serwer jest prywatny. Podaj hasło aby się połączyć."
|
||||||
|
|
||||||
|
#: forms.py:222 forms.py:274
|
||||||
|
msgid "The password you entered is incorrect."
|
||||||
|
msgstr "Wprowadzone hasło jest niepoprawne"
|
||||||
|
|
||||||
|
#: forms.py:237
|
||||||
|
msgid "Link account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:238
|
||||||
|
msgid ""
|
||||||
|
"The account already exists and belongs to me, just link it instead of "
|
||||||
|
"creating."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:255
|
||||||
|
msgid "No such user found."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:290
|
||||||
|
msgid "That account belongs to someone else."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:293
|
||||||
|
msgid "Linking Admin accounts is not allowed."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:322 templates/mumble/mumble.html:65
|
||||||
|
#: templates/mumble/mumble.html.py:148 templates/mumble/mumble.html:278
|
||||||
|
msgid "User Texture"
|
||||||
|
msgstr "Awatar użytkownika"
|
||||||
|
|
||||||
|
#: models.py:63
|
||||||
|
msgid "DBus or ICE base"
|
||||||
|
msgstr "DBus lub baza ICE"
|
||||||
|
|
||||||
|
#: models.py:64
|
||||||
|
msgid ""
|
||||||
|
"Examples: 'net.sourceforge.mumble.murmur' for DBus or 'Meta:tcp -h 127.0.0.1 "
|
||||||
|
"-p 6502' for Ice."
|
||||||
|
msgstr ""
|
||||||
|
"Przykłąd: 'net.sourceforge.mumble.murmur' dla DBus oder 'Meta:tcp -h "
|
||||||
|
"127.0.0.1 -p 6502' dla Ice."
|
||||||
|
|
||||||
|
#: models.py:65
|
||||||
|
msgid "Ice Secret"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:68
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Mumble Server"
|
||||||
|
msgstr "ID użytkownika"
|
||||||
|
|
||||||
|
#: models.py:69
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Mumble Servers"
|
||||||
|
msgstr "ID użytkownika"
|
||||||
|
|
||||||
|
#: models.py:160
|
||||||
|
msgid "Server Name"
|
||||||
|
msgstr "Nazwa serwera"
|
||||||
|
|
||||||
|
#: models.py:161
|
||||||
|
msgid "Server ID"
|
||||||
|
msgstr "Serwer-ID"
|
||||||
|
|
||||||
|
#: models.py:163
|
||||||
|
msgid ""
|
||||||
|
"Hostname or IP address to bind to. You should use a hostname here, because "
|
||||||
|
"it will appear on the global server list."
|
||||||
|
msgstr ""
|
||||||
|
"Nazwa HOSTa lub adres IP. Zalecamy używać nazwy HOSTa, ponieważserwer będzie "
|
||||||
|
"widoczny na globalnej liście serwerów"
|
||||||
|
|
||||||
|
#: models.py:166
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Port number to bind to. Leave empty to auto assign one."
|
||||||
|
msgstr "Numer portu. Wpisz -1 aby użyć wonego portu."
|
||||||
|
|
||||||
|
#: models.py:167
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Server Display Address"
|
||||||
|
msgstr "Adres serwera"
|
||||||
|
|
||||||
|
#: models.py:168
|
||||||
|
msgid ""
|
||||||
|
"This field is only relevant if you are located behind a NAT, and names the "
|
||||||
|
"Hostname or IP address to use in the Channel Viewer and for the global "
|
||||||
|
"server list registration. If not given, the addr and port fields are used. "
|
||||||
|
"If display and bind ports are equal, you can omit it here."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:174
|
||||||
|
msgid "Superuser Password"
|
||||||
|
msgstr "Hasło SuperUser-a"
|
||||||
|
|
||||||
|
#: models.py:177
|
||||||
|
msgid "Website URL"
|
||||||
|
msgstr "Adres strony"
|
||||||
|
|
||||||
|
#: models.py:178
|
||||||
|
msgid "Welcome Message"
|
||||||
|
msgstr "Wiadomość powitalna"
|
||||||
|
|
||||||
|
#: models.py:180
|
||||||
|
msgid "Max. Users"
|
||||||
|
msgstr "Max. Użytkowników"
|
||||||
|
|
||||||
|
#: models.py:181
|
||||||
|
msgid "Bandwidth [Bps]"
|
||||||
|
msgstr "Przepustowość [Bps]"
|
||||||
|
|
||||||
|
#: models.py:182
|
||||||
|
msgid "SSL Certificate"
|
||||||
|
msgstr "SSL-Certyfikat"
|
||||||
|
|
||||||
|
#: models.py:183
|
||||||
|
msgid "SSL Key"
|
||||||
|
msgstr "SSL-Klucz"
|
||||||
|
|
||||||
|
#: models.py:184
|
||||||
|
msgid "Player name regex"
|
||||||
|
msgstr "Zabronione znaki w nazwie użytkownika"
|
||||||
|
|
||||||
|
#: models.py:185
|
||||||
|
msgid "Channel name regex"
|
||||||
|
msgstr "Zabronione znaki w nazwie kanału"
|
||||||
|
|
||||||
|
#: models.py:186
|
||||||
|
msgid "Default channel"
|
||||||
|
msgstr "Główny kanał"
|
||||||
|
|
||||||
|
#: models.py:187
|
||||||
|
msgid "Timeout"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:189
|
||||||
|
msgid "IP Obfuscation"
|
||||||
|
msgstr "IP-maskowanie"
|
||||||
|
|
||||||
|
#: models.py:190
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Require Certificate"
|
||||||
|
msgstr "SSL-Certyfikat"
|
||||||
|
|
||||||
|
#: models.py:191
|
||||||
|
msgid "Maximum length of text messages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:192
|
||||||
|
msgid "Allow HTML to be used in messages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:193
|
||||||
|
msgid "Publish this server via Bonjour"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:194
|
||||||
|
msgid "Boot Server when Murmur starts"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:212 models.py:213
|
||||||
|
msgid "Boot Server"
|
||||||
|
msgstr "Odpal serwer"
|
||||||
|
|
||||||
|
#: models.py:217 models.py:545
|
||||||
|
msgid "Server instance"
|
||||||
|
msgstr "Aktywne serwery"
|
||||||
|
|
||||||
|
#: models.py:218
|
||||||
|
msgid "Server instances"
|
||||||
|
msgstr "Aktywne serwery"
|
||||||
|
|
||||||
|
#: models.py:505 models.py:685
|
||||||
|
msgid "This field must not be updated once the record has been saved."
|
||||||
|
msgstr "To pole nie musi być aktualizowane, było aktualizowane wcześniej."
|
||||||
|
|
||||||
|
#: models.py:542
|
||||||
|
msgid "Mumble player_id"
|
||||||
|
msgstr "ID użytkownika"
|
||||||
|
|
||||||
|
#: models.py:543
|
||||||
|
msgid "User name and Login"
|
||||||
|
msgstr "Nazwa użytkownika"
|
||||||
|
|
||||||
|
#: models.py:544
|
||||||
|
msgid "Login password"
|
||||||
|
msgstr "Hasło"
|
||||||
|
|
||||||
|
#: models.py:546 templates/mumble/mumble.html:293
|
||||||
|
msgid "Account owner"
|
||||||
|
msgstr "Właściciel konta"
|
||||||
|
|
||||||
|
#: models.py:548
|
||||||
|
#, fuzzy
|
||||||
|
msgid "The user's comment."
|
||||||
|
msgstr "Konto użytkownika"
|
||||||
|
|
||||||
|
#: models.py:549
|
||||||
|
msgid "The user's hash."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:553
|
||||||
|
msgid "User account"
|
||||||
|
msgstr "Konto użytkownika"
|
||||||
|
|
||||||
|
#: models.py:554
|
||||||
|
msgid "User accounts"
|
||||||
|
msgstr "Konta użytkowników"
|
||||||
|
|
||||||
|
#: models.py:561
|
||||||
|
#, python-format
|
||||||
|
msgid "Mumble user %(mu)s on %(srv)s owned by Django user %(du)s"
|
||||||
|
msgstr "Użytkownik %(mu)s na %(srv)s właściciel %(du)s"
|
||||||
|
|
||||||
|
#: templates/mumble/list.html:20
|
||||||
|
msgid "No server instances have been configured yet."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:16
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <b>Hint:</b><br />\n"
|
||||||
|
" This area is used to display additional information for each channel "
|
||||||
|
"and player, but requires JavaScript to be\n"
|
||||||
|
" displayed correctly. You will not see the detail pages, but you can "
|
||||||
|
"use all links and forms\n"
|
||||||
|
" that are displayed.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <b>Info</b><br />\n"
|
||||||
|
" To pole jest używane do wyświetlenia dodatkowych informacji dla "
|
||||||
|
"każdegokanału i użytkownika, ale wymaga JavaScript aby\n"
|
||||||
|
" działało poprawnie. Nie zobaczysz detalów strony, lecz możesz używać "
|
||||||
|
"wszystkich linków i formatów\n"
|
||||||
|
" które zostaną wyświetlone.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:31
|
||||||
|
msgid "Website"
|
||||||
|
msgstr "Strona"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:33
|
||||||
|
msgid "Server version"
|
||||||
|
msgstr "Wersja serwera"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:34
|
||||||
|
msgid "Minimal view"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:37
|
||||||
|
msgid "Welcome message"
|
||||||
|
msgstr "Wiadomość powitalna"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:43
|
||||||
|
msgid "Server registration"
|
||||||
|
msgstr "Rejestracja konta na serwerze"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:46
|
||||||
|
msgid "You are registered on this server"
|
||||||
|
msgstr "Masz konto na tym serwerze"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:48
|
||||||
|
msgid "You do not have an account on this server"
|
||||||
|
msgstr "Nie masz konta na tym serwerze"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:57
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <p>You need to be <a href=\"%(login_url)s\">logged in</a> to be able "
|
||||||
|
"to register an account on this Mumble server.</p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <p>Musisz być <a href=\"%(login_url)s\">zalogowany</a> aby móc "
|
||||||
|
"zarejestrować konto na serwerze.</p>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:67
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Sorry, due to a bug in Murmur 1.2.2, displaying and setting the "
|
||||||
|
"Texture is disabled.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:72
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" You can upload an image that you would like to use as your user "
|
||||||
|
"texture here.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" Możesz wysłać awatar jeśli chcesz.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:77
|
||||||
|
msgid "Your current texture is"
|
||||||
|
msgstr "Twój obecny awatar"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:80
|
||||||
|
msgid "You don't currently have a texture set"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:84
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Hint: The texture image <b>needs</b> to be 600x60 in size. If "
|
||||||
|
"you upload an image with\n"
|
||||||
|
" a different size, it will be resized accordingly.<br />\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <b>Dozwolony</b> rozmiar pliku to: 600x60 pikseli. Każdy inny "
|
||||||
|
"rozmiar zostanie automatycznie zmieniony na podany wyĹĽej. "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:103
|
||||||
|
msgid "Server administration"
|
||||||
|
msgstr "Administracja serwerem"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:117
|
||||||
|
msgid "Player"
|
||||||
|
msgstr "Użytkownik"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:119
|
||||||
|
msgid "Online since"
|
||||||
|
msgstr "Czas online"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:120 templates/mumble/player.html:9
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "Zarejestrowany"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:121 templates/mumble/mumble.html.py:136
|
||||||
|
msgid "Admin"
|
||||||
|
msgstr "Administrator"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:122 templates/mumble/player.html:12
|
||||||
|
msgid "Muted"
|
||||||
|
msgstr "Wyciszony mikrofon"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:123 templates/mumble/player.html:18
|
||||||
|
msgid "Deafened"
|
||||||
|
msgstr "Wyciszone słuchawki"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:124 templates/mumble/player.html:21
|
||||||
|
msgid "Muted by self"
|
||||||
|
msgstr "Wyciszył mikrofon"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:125 templates/mumble/player.html:24
|
||||||
|
msgid "Deafened by self"
|
||||||
|
msgstr "Wyciszył słuchawki"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:127
|
||||||
|
#, fuzzy
|
||||||
|
msgid "IP Address"
|
||||||
|
msgstr "Adres serwera"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:131
|
||||||
|
msgid "User"
|
||||||
|
msgstr "Użytkownik"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:134
|
||||||
|
msgid "Full Name"
|
||||||
|
msgstr "Pełna nazwa"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:137
|
||||||
|
msgid "Sign-up date"
|
||||||
|
msgstr "Data rejestracji"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:142
|
||||||
|
#, fuzzy
|
||||||
|
msgid "User Comment"
|
||||||
|
msgstr "Konto użytkownika"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:154 templates/mumble/mumble.html.py:168
|
||||||
|
msgid "Kick user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:160
|
||||||
|
msgid "Reason"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:165
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Ban user"
|
||||||
|
msgstr "Użytkownicy online"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:175
|
||||||
|
msgid "Channel"
|
||||||
|
msgstr "Kanał"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:177
|
||||||
|
msgid "Channel ID"
|
||||||
|
msgstr "ID-kanału"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:179
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "Połącz"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:182
|
||||||
|
msgid "Channel description"
|
||||||
|
msgstr "Opis kanału"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:229
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:265
|
||||||
|
msgid "Server Info"
|
||||||
|
msgstr "Informacje o serwerze"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:266
|
||||||
|
msgid "Registration"
|
||||||
|
msgstr "Rejestracja"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:275
|
||||||
|
msgid "Administration"
|
||||||
|
msgstr "Administracja"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:282
|
||||||
|
msgid "User List"
|
||||||
|
msgstr "Lista użytkowników"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:286
|
||||||
|
msgid "name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:306
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Change password"
|
||||||
|
msgstr "Hasło"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:319
|
||||||
|
msgid "Add"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:331
|
||||||
|
msgid "Save"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:357
|
||||||
|
msgid "Resync with Murmur"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:15
|
||||||
|
msgid "Suppressed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:27
|
||||||
|
msgid "has a User Comment set"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "Enter the ID of the default channel here. The Channel viewer displays the "
|
||||||
|
#~ "ID to server admins on the channel detail page."
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Podaj numer ID głównego kanału. Numer ID można sprawdzić w Channel-Viewer "
|
||||||
|
|
||||||
|
#~ msgid "The admin group was not found in the ACL's groups list!"
|
||||||
|
#~ msgstr "Grupa admin nie istnieje na liście grup ACL"
|
||||||
BIN
mumble/locale/de/LC_MESSAGES/django.mo
Normal file
BIN
mumble/locale/de/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
530
mumble/locale/de/LC_MESSAGES/django.po
Normal file
530
mumble/locale/de/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
# German translation file for Mumble-Django.
|
||||||
|
#
|
||||||
|
# Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
#
|
||||||
|
# Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This package is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Mumble-Django v0.8\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2010-03-14 18:23+0100\n"
|
||||||
|
"PO-Revision-Date: 2010-03-14 18:25\n"
|
||||||
|
"Last-Translator: <de@de.de>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Translated-Using: django-rosetta 0.5.3\n"
|
||||||
|
|
||||||
|
#: admin.py:34 admin.py:51
|
||||||
|
msgid "Master is running"
|
||||||
|
msgstr "Serverprozess läuft"
|
||||||
|
|
||||||
|
#: admin.py:59 models.py:162 templates/mumble/mumble.html:28
|
||||||
|
msgid "Server Address"
|
||||||
|
msgstr "Serveradresse"
|
||||||
|
|
||||||
|
#: admin.py:66 models.py:165
|
||||||
|
msgid "Server Port"
|
||||||
|
msgstr "Serverport"
|
||||||
|
|
||||||
|
#: admin.py:71
|
||||||
|
msgid "Instance is running"
|
||||||
|
msgstr "Serverinstanz läuft"
|
||||||
|
|
||||||
|
#: admin.py:81
|
||||||
|
msgid "Registered users"
|
||||||
|
msgstr "Registrierte Benutzer"
|
||||||
|
|
||||||
|
#: admin.py:91
|
||||||
|
msgid "Online users"
|
||||||
|
msgstr "Benutzer online"
|
||||||
|
|
||||||
|
#: admin.py:101
|
||||||
|
msgid "Channel count"
|
||||||
|
msgstr "Channels"
|
||||||
|
|
||||||
|
#: admin.py:108
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr "Ja"
|
||||||
|
|
||||||
|
#: admin.py:110
|
||||||
|
msgid "No"
|
||||||
|
msgstr "Nein"
|
||||||
|
|
||||||
|
#: admin.py:114
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Öffentlich"
|
||||||
|
|
||||||
|
#: admin.py:132 models.py:631 templates/mumble/mumble.html:223
|
||||||
|
msgid "Admin on root channel"
|
||||||
|
msgstr "Admin im Wurzelkanal"
|
||||||
|
|
||||||
|
#: forms.py:83
|
||||||
|
msgid "Password required to join. Leave empty for public servers."
|
||||||
|
msgstr "Passwort was zum Verbinden benötigt wird. Lasse es leer, wenn der Server öffentlich sein soll."
|
||||||
|
|
||||||
|
#: forms.py:86
|
||||||
|
msgid "If on, IP adresses of the clients are not logged."
|
||||||
|
msgstr "Wenn das an ist werden IP-Adressen der Clients nicht ins Log geschrieben."
|
||||||
|
|
||||||
|
#: forms.py:142
|
||||||
|
#, python-format
|
||||||
|
msgid "Port number %(portno)d is not within the allowed range %(minrange)d - %(maxrange)d"
|
||||||
|
msgstr "Portnummer %(portno)d liegt nicht in %(minrange)d - %(maxrange)d"
|
||||||
|
|
||||||
|
#: forms.py:152
|
||||||
|
msgid "Default config"
|
||||||
|
msgstr "Standard-Einstellungen"
|
||||||
|
|
||||||
|
#: forms.py:165 templates/mumble/offline.html:12
|
||||||
|
msgid "This server is currently offline."
|
||||||
|
msgstr "Dieser Server ist im Moment nicht erreichbar."
|
||||||
|
|
||||||
|
#: forms.py:190
|
||||||
|
msgid "That name is forbidden by the server."
|
||||||
|
msgstr "Dieser Name wird vom Server nicht erlaubt."
|
||||||
|
|
||||||
|
#: forms.py:193 models.py:587
|
||||||
|
msgid "Another player already registered that name."
|
||||||
|
msgstr "Ein anderer Spieler hat sich unter diesem Namen bereits registriert."
|
||||||
|
|
||||||
|
#: forms.py:201 forms.py:307 models.py:589
|
||||||
|
msgid "Cannot register player without a password!"
|
||||||
|
msgstr "Kann Account nicht ohne Passwort registrieren!"
|
||||||
|
|
||||||
|
#: forms.py:213 models.py:179
|
||||||
|
msgid "Server Password"
|
||||||
|
msgstr "Serverpasswort"
|
||||||
|
|
||||||
|
#: forms.py:214
|
||||||
|
msgid "This server is private and protected mode is active. Please enter the server password."
|
||||||
|
msgstr "Dieser Server ist privat und der Registrierungsschutz ist aktiv. Bitte gib das Serverpasswort ein."
|
||||||
|
|
||||||
|
#: forms.py:222 forms.py:274
|
||||||
|
msgid "The password you entered is incorrect."
|
||||||
|
msgstr "Das eingegebene Passwort ist falsch"
|
||||||
|
|
||||||
|
#: forms.py:237
|
||||||
|
msgid "Link account"
|
||||||
|
msgstr "Accounts verknüpfen"
|
||||||
|
|
||||||
|
#: forms.py:238
|
||||||
|
msgid "The account already exists and belongs to me, just link it instead of creating."
|
||||||
|
msgstr "Dieser Account existiert bereits und gehört mir. Verknüpfe die Konten nur, anstatt ein neues zu erstellen."
|
||||||
|
|
||||||
|
#: forms.py:255
|
||||||
|
msgid "No such user found."
|
||||||
|
msgstr "Benutzer nicht gefunden."
|
||||||
|
|
||||||
|
#: forms.py:290
|
||||||
|
msgid "That account belongs to someone else."
|
||||||
|
msgstr "Dieser Account gehört jemand anderem."
|
||||||
|
|
||||||
|
#: forms.py:293
|
||||||
|
msgid "Linking Admin accounts is not allowed."
|
||||||
|
msgstr "Admin-Accounts zu verknüpfen ist nicht erlaubt."
|
||||||
|
|
||||||
|
#: forms.py:322 templates/mumble/mumble.html:65
|
||||||
|
#: templates/mumble/mumble.html.py:148 templates/mumble/mumble.html:278
|
||||||
|
msgid "User Texture"
|
||||||
|
msgstr "Benutzertextur"
|
||||||
|
|
||||||
|
#: models.py:63
|
||||||
|
msgid "DBus or ICE base"
|
||||||
|
msgstr "DBus- oder ICE-String"
|
||||||
|
|
||||||
|
#: models.py:64
|
||||||
|
msgid "Examples: 'net.sourceforge.mumble.murmur' for DBus or 'Meta:tcp -h 127.0.0.1 -p 6502' for Ice."
|
||||||
|
msgstr "Beispiele: 'net.sourceforge.mumble.murmur' für DBus oder 'Meta:tcp -h 127.0.0.1 -p 6502' für Ice."
|
||||||
|
|
||||||
|
#: models.py:65
|
||||||
|
msgid "Ice Secret"
|
||||||
|
msgstr "Ice-Passwort"
|
||||||
|
|
||||||
|
#: models.py:68 models.py:159
|
||||||
|
msgid "Mumble Server"
|
||||||
|
msgstr "Mumble-Server"
|
||||||
|
|
||||||
|
#: models.py:69
|
||||||
|
msgid "Mumble Servers"
|
||||||
|
msgstr "Mumble-Server"
|
||||||
|
|
||||||
|
#: models.py:160
|
||||||
|
msgid "Server Name"
|
||||||
|
msgstr "Servername"
|
||||||
|
|
||||||
|
#: models.py:161
|
||||||
|
msgid "Server ID"
|
||||||
|
msgstr "Server-ID"
|
||||||
|
|
||||||
|
#: models.py:163
|
||||||
|
msgid "Hostname or IP address to bind to. You should use a hostname here, because it will appear on the global server list."
|
||||||
|
msgstr "Hostname oder IP-Adresse unter der der Server erreichbar sein soll. Du solltest einen Hostname verwenden, da dieses Feld in der globalen Serverliste erscheint."
|
||||||
|
|
||||||
|
#: models.py:166
|
||||||
|
msgid "Port number to bind to. Leave empty to auto assign one."
|
||||||
|
msgstr "Portnummer auf die gebunden werden soll. Lasse das Feld leer um automatisch eine zuzuweisen."
|
||||||
|
|
||||||
|
#: models.py:167
|
||||||
|
msgid "Server Display Address"
|
||||||
|
msgstr "Angezeigte Adresse"
|
||||||
|
|
||||||
|
#: models.py:168
|
||||||
|
msgid "This field is only relevant if you are located behind a NAT, and names the Hostname or IP address to use in the Channel Viewer and for the global server list registration. If not given, the addr and port fields are used. If display and bind ports are equal, you can omit it here."
|
||||||
|
msgstr "Dieses Feld ist nur relevant wenn der Server hinter einem NAT steht, und gibt dann den Hostnamen oder die IP-Adresse die im Channel-Viewer und für die Registrierung in der Serverliste benutzt werden soll. Ist dieses Feld leer, werden die Angaben Addresse und Port stattdessen benutzt. Wenn die Ports identisch sind, kannst du den Port hier weglassen."
|
||||||
|
|
||||||
|
#: models.py:174
|
||||||
|
msgid "Superuser Password"
|
||||||
|
msgstr "SuperUser-Passwort"
|
||||||
|
|
||||||
|
#: models.py:177
|
||||||
|
msgid "Website URL"
|
||||||
|
msgstr "URL der Webseite"
|
||||||
|
|
||||||
|
#: models.py:178
|
||||||
|
msgid "Welcome Message"
|
||||||
|
msgstr "Willkommensnachricht"
|
||||||
|
|
||||||
|
#: models.py:180
|
||||||
|
msgid "Max. Users"
|
||||||
|
msgstr "Max. Benutzer"
|
||||||
|
|
||||||
|
#: models.py:181
|
||||||
|
msgid "Bandwidth [Bps]"
|
||||||
|
msgstr "Bandbreite [Bps]"
|
||||||
|
|
||||||
|
#: models.py:182
|
||||||
|
msgid "SSL Certificate"
|
||||||
|
msgstr "SSL-Zertifikat"
|
||||||
|
|
||||||
|
#: models.py:183
|
||||||
|
msgid "SSL Key"
|
||||||
|
msgstr "SSL-Schlüssel"
|
||||||
|
|
||||||
|
#: models.py:184
|
||||||
|
msgid "Player name regex"
|
||||||
|
msgstr "Regex für Spielernamen"
|
||||||
|
|
||||||
|
#: models.py:185
|
||||||
|
msgid "Channel name regex"
|
||||||
|
msgstr "Regex für Channelnamen"
|
||||||
|
|
||||||
|
#: models.py:186
|
||||||
|
msgid "Default channel"
|
||||||
|
msgstr "Standardchannel"
|
||||||
|
|
||||||
|
#: models.py:187
|
||||||
|
msgid "Timeout"
|
||||||
|
msgstr "Timeout"
|
||||||
|
|
||||||
|
#: models.py:189
|
||||||
|
msgid "IP Obfuscation"
|
||||||
|
msgstr "IP-Adressen anonymisieren"
|
||||||
|
|
||||||
|
#: models.py:190
|
||||||
|
msgid "Require Certificate"
|
||||||
|
msgstr "SSL-Zertifikat erzwingen"
|
||||||
|
|
||||||
|
#: models.py:191
|
||||||
|
msgid "Maximum length of text messages"
|
||||||
|
msgstr "Maximale Länge von Textnachrichten"
|
||||||
|
|
||||||
|
#: models.py:192
|
||||||
|
msgid "Allow HTML to be used in messages"
|
||||||
|
msgstr "Erlaube HTML in Nachrichten"
|
||||||
|
|
||||||
|
#: models.py:193
|
||||||
|
msgid "Publish this server via Bonjour"
|
||||||
|
msgstr "Server via Bonjour bekannt machen"
|
||||||
|
|
||||||
|
#: models.py:194
|
||||||
|
msgid "Boot Server when Murmur starts"
|
||||||
|
msgstr "Instanz starten wenn Murmur startet"
|
||||||
|
|
||||||
|
#: models.py:212 models.py:213
|
||||||
|
msgid "Boot Server"
|
||||||
|
msgstr "Server starten"
|
||||||
|
|
||||||
|
#: models.py:217 models.py:550
|
||||||
|
msgid "Server instance"
|
||||||
|
msgstr "Serverinstanz"
|
||||||
|
|
||||||
|
#: models.py:218
|
||||||
|
msgid "Server instances"
|
||||||
|
msgstr "Serverinstanzen"
|
||||||
|
|
||||||
|
#: models.py:510 models.py:690
|
||||||
|
msgid "This field must not be updated once the record has been saved."
|
||||||
|
msgstr "Dieses Feld darf nicht mehr verändert werden, nachdem der Eintrag zum ersten Mal gespeichert wurde."
|
||||||
|
|
||||||
|
#: models.py:547
|
||||||
|
msgid "Mumble player_id"
|
||||||
|
msgstr "ID des Spielers in Murmur"
|
||||||
|
|
||||||
|
#: models.py:548
|
||||||
|
msgid "User name and Login"
|
||||||
|
msgstr "Benutzername und Login"
|
||||||
|
|
||||||
|
#: models.py:549
|
||||||
|
msgid "Login password"
|
||||||
|
msgstr "Passwort"
|
||||||
|
|
||||||
|
#: models.py:551 templates/mumble/mumble.html:293
|
||||||
|
msgid "Account owner"
|
||||||
|
msgstr "Accountbesitzer"
|
||||||
|
|
||||||
|
#: models.py:553
|
||||||
|
msgid "The user's comment."
|
||||||
|
msgstr "Benutzer-Kommentar"
|
||||||
|
|
||||||
|
#: models.py:554
|
||||||
|
msgid "The user's hash."
|
||||||
|
msgstr "Signatur des Zertifikats"
|
||||||
|
|
||||||
|
#: models.py:558
|
||||||
|
msgid "User account"
|
||||||
|
msgstr "Benutzerkonto"
|
||||||
|
|
||||||
|
#: models.py:559
|
||||||
|
msgid "User accounts"
|
||||||
|
msgstr "Benutzerkonten"
|
||||||
|
|
||||||
|
#: models.py:566
|
||||||
|
#, python-format
|
||||||
|
msgid "Mumble user %(mu)s on %(srv)s owned by Django user %(du)s"
|
||||||
|
msgstr "Benutzeraccount %(mu)s auf %(srv)s mit Besitzer %(du)s"
|
||||||
|
|
||||||
|
#: templates/mumble/list.html:20
|
||||||
|
msgid "No server instances have been configured yet."
|
||||||
|
msgstr "Es sind noch keine Serverinstanzen konfiguriert."
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:16
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <b>Hint:</b><br />\n"
|
||||||
|
" This area is used to display additional information for each channel and player, but requires JavaScript to be\n"
|
||||||
|
" displayed correctly. You will not see the detail pages, but you can use all links and forms\n"
|
||||||
|
" that are displayed.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <b>Hinweis:</b><br />\n"
|
||||||
|
" Dieser Bereich wird genutzt um zusätzliche Informationen über jeden Channel und Spieler anzuzeigen, erfordert aber JavaScript um\n"
|
||||||
|
" richtig angezeigt zu werden. Du wirst zwar die Detailseiten nicht sehen, kannst aber alle sichtbaren Formulare benutzen.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:31
|
||||||
|
msgid "Website"
|
||||||
|
msgstr "Webseite"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:33
|
||||||
|
msgid "Server version"
|
||||||
|
msgstr "Serverversion"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:34
|
||||||
|
msgid "Minimal view"
|
||||||
|
msgstr "Minimalansicht"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:37
|
||||||
|
msgid "Welcome message"
|
||||||
|
msgstr "Willkommensnachricht"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:43
|
||||||
|
msgid "Server registration"
|
||||||
|
msgstr "Benutzerregistrierung"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:46
|
||||||
|
msgid "You are registered on this server"
|
||||||
|
msgstr "Du bist auf diesem Server registriert"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:48
|
||||||
|
msgid "You do not have an account on this server"
|
||||||
|
msgstr "Du bist auf diesem Server nicht registriert"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:57
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <p>You need to be <a href=\"%(login_url)s\">logged in</a> to be able to register an account on this Mumble server.</p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <p>Du musst <a href=\"%(login_url)s\">eingeloggt</a> sein um auf diesem Mumble-Server einen Account registrieren zu können.</p>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:67
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Sorry, due to a bug in Murmur 1.2.2, displaying and setting the Texture is disabled.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"Entschuldigung, aufgrund eines Fehlers in Murmur 1.2.2 ist die Texturanzeige sowie das Hochladen für diese Version deaktiviert."
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:72
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" You can upload an image that you would like to use as your user texture here.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"Du kannst hier ein Bild hochladen, das als deine Benutzertextur angezeigt werden soll."
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:77
|
||||||
|
msgid "Your current texture is"
|
||||||
|
msgstr "Deine momentane Textur ist diese"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:80
|
||||||
|
msgid "You don't currently have a texture set"
|
||||||
|
msgstr "Du hast momentan keine Textur gesetzt"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:84
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Hint: The texture image <b>needs</b> to be 600x60 in size. If you upload an image with\n"
|
||||||
|
" a different size, it will be resized accordingly.<br />\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"Hinweis: Das Texturbild <b>muss</b> die Größe 600x60 haben. Wenn du ein Bild mit einer anderen Größe hochlädst, wird es automatisch zurecht geschnitten.<br />"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:103
|
||||||
|
msgid "Server administration"
|
||||||
|
msgstr "Server-Administration"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:117
|
||||||
|
msgid "Player"
|
||||||
|
msgstr "Spieler"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:119
|
||||||
|
msgid "Online since"
|
||||||
|
msgstr "Online seit"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:120 templates/mumble/player.html:9
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "Authentifiziert"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:121 templates/mumble/mumble.html.py:136
|
||||||
|
msgid "Admin"
|
||||||
|
msgstr "Administrator"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:122 templates/mumble/player.html:12
|
||||||
|
msgid "Muted"
|
||||||
|
msgstr "Stummgestellt"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:123 templates/mumble/player.html:18
|
||||||
|
msgid "Deafened"
|
||||||
|
msgstr "Taubgestellt"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:124 templates/mumble/player.html:21
|
||||||
|
msgid "Muted by self"
|
||||||
|
msgstr "Selbst stummgestellt"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:125 templates/mumble/player.html:24
|
||||||
|
msgid "Deafened by self"
|
||||||
|
msgstr "Selbst taubgestellt"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:127
|
||||||
|
msgid "IP Address"
|
||||||
|
msgstr "IP-Adresse"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:131
|
||||||
|
msgid "User"
|
||||||
|
msgstr "Benutzer"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:134
|
||||||
|
msgid "Full Name"
|
||||||
|
msgstr "Vollständiger Name"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:137
|
||||||
|
msgid "Sign-up date"
|
||||||
|
msgstr "Datum der Anmeldung"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:142
|
||||||
|
msgid "User Comment"
|
||||||
|
msgstr "Benutzer-Kommentar"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:154 templates/mumble/mumble.html.py:168
|
||||||
|
msgid "Kick user"
|
||||||
|
msgstr "Benutzer kicken"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:160
|
||||||
|
msgid "Reason"
|
||||||
|
msgstr "Begründung"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:165
|
||||||
|
msgid "Ban user"
|
||||||
|
msgstr "Benutzer bannen"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:175
|
||||||
|
msgid "Channel"
|
||||||
|
msgstr "Kanal"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:177
|
||||||
|
msgid "Channel ID"
|
||||||
|
msgstr "Kanal-ID"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:179
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "Verbinden"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:182
|
||||||
|
msgid "Channel description"
|
||||||
|
msgstr "Beschreibung des Kanals"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:229
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Löschen"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:265
|
||||||
|
msgid "Server Info"
|
||||||
|
msgstr "Server-Infos"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:266
|
||||||
|
msgid "Registration"
|
||||||
|
msgstr "Registrierung"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:275
|
||||||
|
msgid "Administration"
|
||||||
|
msgstr "Administration"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:282
|
||||||
|
msgid "User List"
|
||||||
|
msgstr "Benutzerliste"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:286
|
||||||
|
msgid "name"
|
||||||
|
msgstr "Name"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:306
|
||||||
|
msgid "Change password"
|
||||||
|
msgstr "Passwort ändern"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:319
|
||||||
|
msgid "Add"
|
||||||
|
msgstr "Hinzufügen"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:331
|
||||||
|
msgid "Save"
|
||||||
|
msgstr "Speichern"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:357
|
||||||
|
msgid "Resync with Murmur"
|
||||||
|
msgstr "Liste neu laden"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:15
|
||||||
|
msgid "Suppressed"
|
||||||
|
msgstr "Unterdrückt"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:27
|
||||||
|
msgid "has a User Comment set"
|
||||||
|
msgstr "hat einen Benutzer-Kommentar gesetzt"
|
||||||
BIN
mumble/locale/fr/LC_MESSAGES/django.mo
Normal file
BIN
mumble/locale/fr/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
568
mumble/locale/fr/LC_MESSAGES/django.po
Executable file
568
mumble/locale/fr/LC_MESSAGES/django.po
Executable file
@@ -0,0 +1,568 @@
|
|||||||
|
# French translation file for Mumble-Django.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009, Antoine Bertin <bertinantoine@neuf.fr>
|
||||||
|
#
|
||||||
|
# Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This package is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Mumble-Django v2.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2010-03-14 18:23+0100\n"
|
||||||
|
"PO-Revision-Date: 2010-03-14 23:10+0100\n"
|
||||||
|
"Last-Translator: Antoine Bertin <bertinantoine@neuf.fr>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Translated-Using: django-rosetta 0.5.3\n"
|
||||||
|
|
||||||
|
#: admin.py:34
|
||||||
|
#: admin.py:51
|
||||||
|
msgid "Master is running"
|
||||||
|
msgstr "Le serveur fonctionne"
|
||||||
|
|
||||||
|
#: admin.py:59
|
||||||
|
#: models.py:162
|
||||||
|
#: templates/mumble/mumble.html:28
|
||||||
|
msgid "Server Address"
|
||||||
|
msgstr "Adresse du serveur"
|
||||||
|
|
||||||
|
#: admin.py:66
|
||||||
|
#: models.py:165
|
||||||
|
msgid "Server Port"
|
||||||
|
msgstr "Port du serveur"
|
||||||
|
|
||||||
|
#: admin.py:71
|
||||||
|
msgid "Instance is running"
|
||||||
|
msgstr "L'instance fonctionne"
|
||||||
|
|
||||||
|
#: admin.py:81
|
||||||
|
msgid "Registered users"
|
||||||
|
msgstr "Utilisateurs enregistrés"
|
||||||
|
|
||||||
|
#: admin.py:91
|
||||||
|
msgid "Online users"
|
||||||
|
msgstr "Utilisateurs en ligne"
|
||||||
|
|
||||||
|
#: admin.py:101
|
||||||
|
msgid "Channel count"
|
||||||
|
msgstr "Nombre de canaux"
|
||||||
|
|
||||||
|
#: admin.py:108
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr "Oui"
|
||||||
|
|
||||||
|
#: admin.py:110
|
||||||
|
msgid "No"
|
||||||
|
msgstr "Non"
|
||||||
|
|
||||||
|
#: admin.py:114
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Public"
|
||||||
|
|
||||||
|
#: admin.py:132
|
||||||
|
#: models.py:631
|
||||||
|
#: templates/mumble/mumble.html:223
|
||||||
|
msgid "Admin on root channel"
|
||||||
|
msgstr "Admin sur le canal racine"
|
||||||
|
|
||||||
|
#: forms.py:83
|
||||||
|
msgid "Password required to join. Leave empty for public servers."
|
||||||
|
msgstr "Mot de passe requis pour se connecter. Laisser vide pour un serveur public."
|
||||||
|
|
||||||
|
#: forms.py:86
|
||||||
|
msgid "If on, IP adresses of the clients are not logged."
|
||||||
|
msgstr "Si oui, les adresses IP des utilisateurs ne seront pas loggées."
|
||||||
|
|
||||||
|
#: forms.py:142
|
||||||
|
#, python-format
|
||||||
|
msgid "Port number %(portno)d is not within the allowed range %(minrange)d - %(maxrange)d"
|
||||||
|
msgstr "Le port %(portno)d n'est pas dans la plage autorisée %(minrange)d - %(maxrange)d"
|
||||||
|
|
||||||
|
#: forms.py:152
|
||||||
|
msgid "Default config"
|
||||||
|
msgstr "Configuration par défaut"
|
||||||
|
|
||||||
|
#: forms.py:165
|
||||||
|
#: templates/mumble/offline.html:12
|
||||||
|
msgid "This server is currently offline."
|
||||||
|
msgstr "Ce serveur est offline."
|
||||||
|
|
||||||
|
#: forms.py:190
|
||||||
|
msgid "That name is forbidden by the server."
|
||||||
|
msgstr "Ce nom est interdit par le serveur."
|
||||||
|
|
||||||
|
#: forms.py:193
|
||||||
|
#: models.py:587
|
||||||
|
msgid "Another player already registered that name."
|
||||||
|
msgstr "Ce nom d'utilisateurs est déjà pris."
|
||||||
|
|
||||||
|
#: forms.py:201
|
||||||
|
#: forms.py:307
|
||||||
|
#: models.py:589
|
||||||
|
msgid "Cannot register player without a password!"
|
||||||
|
msgstr "Impossible d'enregistrer un utilisateur sans mot de passe!"
|
||||||
|
|
||||||
|
#: forms.py:213
|
||||||
|
#: models.py:179
|
||||||
|
msgid "Server Password"
|
||||||
|
msgstr "Mot de passe du serveur"
|
||||||
|
|
||||||
|
#: forms.py:214
|
||||||
|
msgid "This server is private and protected mode is active. Please enter the server password."
|
||||||
|
msgstr "Ce serveur est privé et le mode protégé est actif. Merci de saisir le mot de passe du serveur."
|
||||||
|
|
||||||
|
#: forms.py:222
|
||||||
|
#: forms.py:274
|
||||||
|
msgid "The password you entered is incorrect."
|
||||||
|
msgstr "Le mot de passe saisi est incorrect."
|
||||||
|
|
||||||
|
#: forms.py:237
|
||||||
|
msgid "Link account"
|
||||||
|
msgstr "Lier le compte"
|
||||||
|
|
||||||
|
#: forms.py:238
|
||||||
|
msgid "The account already exists and belongs to me, just link it instead of creating."
|
||||||
|
msgstr "Ce compte existe déjà et m'apartient. Vous pouvez le lier."
|
||||||
|
|
||||||
|
#: forms.py:255
|
||||||
|
msgid "No such user found."
|
||||||
|
msgstr "Aucun utilisateur trouvé."
|
||||||
|
|
||||||
|
#: forms.py:290
|
||||||
|
msgid "That account belongs to someone else."
|
||||||
|
msgstr "Ce compte appartient à quelqu'un d'autre."
|
||||||
|
|
||||||
|
#: forms.py:293
|
||||||
|
msgid "Linking Admin accounts is not allowed."
|
||||||
|
msgstr "Les liaisons de comptes Admin ne sont pas autorisées."
|
||||||
|
|
||||||
|
#: forms.py:322
|
||||||
|
#: templates/mumble/mumble.html:65
|
||||||
|
#: templates/mumble/mumble.html.py:148
|
||||||
|
#: templates/mumble/mumble.html:278
|
||||||
|
msgid "User Texture"
|
||||||
|
msgstr "Avatar de l'utilisateur"
|
||||||
|
|
||||||
|
#: models.py:63
|
||||||
|
msgid "DBus or ICE base"
|
||||||
|
msgstr "DBus ou ICE base"
|
||||||
|
|
||||||
|
#: models.py:64
|
||||||
|
msgid "Examples: 'net.sourceforge.mumble.murmur' for DBus or 'Meta:tcp -h 127.0.0.1 -p 6502' for Ice."
|
||||||
|
msgstr "Exemples: 'net.sourceforge.mumble.murmur' pour DBus ou 'Meta:tcp -h 127.0.0.1 -p 6502' pour Ice."
|
||||||
|
|
||||||
|
#: models.py:65
|
||||||
|
msgid "Ice Secret"
|
||||||
|
msgstr "Ice Secret"
|
||||||
|
|
||||||
|
#: models.py:68
|
||||||
|
#: models.py:159
|
||||||
|
msgid "Mumble Server"
|
||||||
|
msgstr "Serveur Mumble"
|
||||||
|
|
||||||
|
#: models.py:69
|
||||||
|
msgid "Mumble Servers"
|
||||||
|
msgstr "Serveurs Mumble"
|
||||||
|
|
||||||
|
#: models.py:160
|
||||||
|
msgid "Server Name"
|
||||||
|
msgstr "Nom du serveur"
|
||||||
|
|
||||||
|
#: models.py:161
|
||||||
|
msgid "Server ID"
|
||||||
|
msgstr "ID du serveur"
|
||||||
|
|
||||||
|
#: models.py:163
|
||||||
|
msgid "Hostname or IP address to bind to. You should use a hostname here, because it will appear on the global server list."
|
||||||
|
msgstr "Nom de domaine ou adresse IP à associer au serveur. Il est préférable d'utiliser un nom de domaine car il sera visible dans la liste des serveurs."
|
||||||
|
|
||||||
|
#: models.py:166
|
||||||
|
msgid "Port number to bind to. Leave empty to auto assign one."
|
||||||
|
msgstr "Numéro de port du serveur. Laissez vide pour assigner automatiquement."
|
||||||
|
|
||||||
|
#: models.py:167
|
||||||
|
msgid "Server Display Address"
|
||||||
|
msgstr "Adresse du serveur à afficher"
|
||||||
|
|
||||||
|
#: models.py:168
|
||||||
|
msgid "This field is only relevant if you are located behind a NAT, and names the Hostname or IP address to use in the Channel Viewer and for the global server list registration. If not given, the addr and port fields are used. If display and bind ports are equal, you can omit it here."
|
||||||
|
msgstr "Ce champ n'est valable que si vous êtes derrière un NAT. Nom de domaine ou adresse IP à utiliser dans le Channel Viewer et pour la liste des serveurs. Si vide, les champs d'adresse et de port sont utilisés. Si le port d'affichage et le port du serveur sont égaux, vous pouvez laisser vide."
|
||||||
|
|
||||||
|
#: models.py:174
|
||||||
|
msgid "Superuser Password"
|
||||||
|
msgstr "Mot de passe de Superuser"
|
||||||
|
|
||||||
|
#: models.py:177
|
||||||
|
msgid "Website URL"
|
||||||
|
msgstr "URL du site web"
|
||||||
|
|
||||||
|
#: models.py:178
|
||||||
|
msgid "Welcome Message"
|
||||||
|
msgstr "Message de bienvenue"
|
||||||
|
|
||||||
|
#: models.py:180
|
||||||
|
msgid "Max. Users"
|
||||||
|
msgstr "Utilisateurs Max."
|
||||||
|
|
||||||
|
#: models.py:181
|
||||||
|
msgid "Bandwidth [Bps]"
|
||||||
|
msgstr "Bande passante [Bps]"
|
||||||
|
|
||||||
|
#: models.py:182
|
||||||
|
msgid "SSL Certificate"
|
||||||
|
msgstr "Certificat SSL"
|
||||||
|
|
||||||
|
#: models.py:183
|
||||||
|
msgid "SSL Key"
|
||||||
|
msgstr "Clé SSL"
|
||||||
|
|
||||||
|
#: models.py:184
|
||||||
|
msgid "Player name regex"
|
||||||
|
msgstr "Regex pour le nom des utilisateurs"
|
||||||
|
|
||||||
|
#: models.py:185
|
||||||
|
msgid "Channel name regex"
|
||||||
|
msgstr "Regex pour le nom des canaux"
|
||||||
|
|
||||||
|
#: models.py:186
|
||||||
|
msgid "Default channel"
|
||||||
|
msgstr "Canal par défaut"
|
||||||
|
|
||||||
|
#: models.py:187
|
||||||
|
msgid "Timeout"
|
||||||
|
msgstr "Timeout"
|
||||||
|
|
||||||
|
#: models.py:189
|
||||||
|
msgid "IP Obfuscation"
|
||||||
|
msgstr "IP Anonyme"
|
||||||
|
|
||||||
|
#: models.py:190
|
||||||
|
msgid "Require Certificate"
|
||||||
|
msgstr "Certificat requis"
|
||||||
|
|
||||||
|
#: models.py:191
|
||||||
|
msgid "Maximum length of text messages"
|
||||||
|
msgstr "Longueur maximum des messages textes"
|
||||||
|
|
||||||
|
#: models.py:192
|
||||||
|
msgid "Allow HTML to be used in messages"
|
||||||
|
msgstr "Permettre le HTML dans les messages"
|
||||||
|
|
||||||
|
#: models.py:193
|
||||||
|
msgid "Publish this server via Bonjour"
|
||||||
|
msgstr "Publier ce serveur via Bonjour"
|
||||||
|
|
||||||
|
#: models.py:194
|
||||||
|
msgid "Boot Server when Murmur starts"
|
||||||
|
msgstr "Démarrer ce serveur au lancement de Murmur"
|
||||||
|
|
||||||
|
#: models.py:212
|
||||||
|
#: models.py:213
|
||||||
|
msgid "Boot Server"
|
||||||
|
msgstr "Serveur démarré automatiquement"
|
||||||
|
|
||||||
|
#: models.py:217
|
||||||
|
#: models.py:550
|
||||||
|
msgid "Server instance"
|
||||||
|
msgstr "Instance du serveur"
|
||||||
|
|
||||||
|
#: models.py:218
|
||||||
|
msgid "Server instances"
|
||||||
|
msgstr "Instances du serveur"
|
||||||
|
|
||||||
|
#: models.py:510
|
||||||
|
#: models.py:690
|
||||||
|
msgid "This field must not be updated once the record has been saved."
|
||||||
|
msgstr "Ce champ ne dois pas être modifié une fois l'enregistrement sauvegardé."
|
||||||
|
|
||||||
|
#: models.py:547
|
||||||
|
msgid "Mumble player_id"
|
||||||
|
msgstr "ID de l'utilisateur"
|
||||||
|
|
||||||
|
#: models.py:548
|
||||||
|
msgid "User name and Login"
|
||||||
|
msgstr "Nom d'utilisateur et login"
|
||||||
|
|
||||||
|
#: models.py:549
|
||||||
|
msgid "Login password"
|
||||||
|
msgstr "Mot de passe"
|
||||||
|
|
||||||
|
#: models.py:551
|
||||||
|
#: templates/mumble/mumble.html:293
|
||||||
|
msgid "Account owner"
|
||||||
|
msgstr "Propriétaire du compte"
|
||||||
|
|
||||||
|
#: models.py:553
|
||||||
|
msgid "The user's comment."
|
||||||
|
msgstr "Commentaire de l'utilisateur"
|
||||||
|
|
||||||
|
#: models.py:554
|
||||||
|
msgid "The user's hash."
|
||||||
|
msgstr "Hash de l'utilisateur"
|
||||||
|
|
||||||
|
#: models.py:558
|
||||||
|
msgid "User account"
|
||||||
|
msgstr "Compte d'utilisateur"
|
||||||
|
|
||||||
|
#: models.py:559
|
||||||
|
msgid "User accounts"
|
||||||
|
msgstr "Comptes d'utilisateur"
|
||||||
|
|
||||||
|
#: models.py:566
|
||||||
|
#, python-format
|
||||||
|
msgid "Mumble user %(mu)s on %(srv)s owned by Django user %(du)s"
|
||||||
|
msgstr "L'utilisateur Mumble %(mu)s sur %(srv)s correspond à l'utilisateur Django %(du)s"
|
||||||
|
|
||||||
|
#: templates/mumble/list.html:20
|
||||||
|
msgid "No server instances have been configured yet."
|
||||||
|
msgstr "Aucune instance du serveur n'a été configurée pour l'instant."
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:16
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <b>Hint:</b><br />\n"
|
||||||
|
" This area is used to display additional information for each channel and player, but requires JavaScript to be\n"
|
||||||
|
" displayed correctly. You will not see the detail pages, but you can use all links and forms\n"
|
||||||
|
" that are displayed.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <b>Remarque :</b><br />\n"
|
||||||
|
" Cette zone est utilisée pour affichier des informations supplémentaires pour chaque canal et utilisateur.\n"
|
||||||
|
" JavaScript est nécessaire pour un meilleur affichage. Vous ne verrez pas les pages de détail mais vous pouvez\n"
|
||||||
|
" utiliser les liens et les formulaires.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:31
|
||||||
|
msgid "Website"
|
||||||
|
msgstr "Site web"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:33
|
||||||
|
msgid "Server version"
|
||||||
|
msgstr "Version du serveur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:34
|
||||||
|
msgid "Minimal view"
|
||||||
|
msgstr "Vue minimaliste"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:37
|
||||||
|
msgid "Welcome message"
|
||||||
|
msgstr "Message de bienvenue"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:43
|
||||||
|
msgid "Server registration"
|
||||||
|
msgstr "Inscription serveur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:46
|
||||||
|
msgid "You are registered on this server"
|
||||||
|
msgstr "Vous êtes authentifié sur ce serveur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:48
|
||||||
|
msgid "You do not have an account on this server"
|
||||||
|
msgstr "Vous n'avez pas de compte sur ce serveur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:57
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <p>You need to be <a href=\"%(login_url)s\">logged in</a> to be able to register an account on this Mumble server.</p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <p>Vous devez être <a href=\"%(login_url)s\">authentifié</a> pour pouvoir inscrire un compte sur ce serveur Mumble</p>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:67
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Sorry, due to a bug in Murmur 1.2.2, displaying and setting the Texture is disabled.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" Désolé, à cause d'un bug dans Murmur 1.2.2, l'affichage et le paramétrage des avatars sont désactivé.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:72
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" You can upload an image that you would like to use as your user texture here.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" Ici vous pouvez uploader l'avatar que vous souhaitez utiliser.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:77
|
||||||
|
msgid "Your current texture is"
|
||||||
|
msgstr "Votre avatar est"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:80
|
||||||
|
msgid "You don't currently have a texture set"
|
||||||
|
msgstr "Vous n'avez pas d'avatar pour l'instant"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:84
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Hint: The texture image <b>needs</b> to be 600x60 in size. If you upload an image with\n"
|
||||||
|
" a different size, it will be resized accordingly.<br />\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" Remarque : La taille de l'avatar <b>doit être</b> de 600x60. Si vous uploadez une image\n"
|
||||||
|
" d'une taille différente, elle sera redimensionnée.<br />\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:103
|
||||||
|
msgid "Server administration"
|
||||||
|
msgstr "Administration du serveur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:117
|
||||||
|
msgid "Player"
|
||||||
|
msgstr "Utilisateur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:119
|
||||||
|
msgid "Online since"
|
||||||
|
msgstr "En ligne depuis"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:120
|
||||||
|
#: templates/mumble/player.html:9
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "Authentifié"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:121
|
||||||
|
#: templates/mumble/mumble.html.py:136
|
||||||
|
msgid "Admin"
|
||||||
|
msgstr "Admin"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:122
|
||||||
|
#: templates/mumble/player.html:12
|
||||||
|
msgid "Muted"
|
||||||
|
msgstr "Muet"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:123
|
||||||
|
#: templates/mumble/player.html:18
|
||||||
|
msgid "Deafened"
|
||||||
|
msgstr "Sourd"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:124
|
||||||
|
#: templates/mumble/player.html:21
|
||||||
|
msgid "Muted by self"
|
||||||
|
msgstr "Devenir muet"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:125
|
||||||
|
#: templates/mumble/player.html:24
|
||||||
|
msgid "Deafened by self"
|
||||||
|
msgstr "Devenir sourd"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:127
|
||||||
|
msgid "IP Address"
|
||||||
|
msgstr "Adresse IP du serveur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:131
|
||||||
|
msgid "User"
|
||||||
|
msgstr "Utilisateur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:134
|
||||||
|
msgid "Full Name"
|
||||||
|
msgstr "Nom"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:137
|
||||||
|
msgid "Sign-up date"
|
||||||
|
msgstr "Date d'enregistrement"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:142
|
||||||
|
msgid "User Comment"
|
||||||
|
msgstr "Commentaire d'utilisateur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:154
|
||||||
|
#: templates/mumble/mumble.html.py:168
|
||||||
|
msgid "Kick user"
|
||||||
|
msgstr "Kicker l'utilisateur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:160
|
||||||
|
msgid "Reason"
|
||||||
|
msgstr "Raison"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:165
|
||||||
|
msgid "Ban user"
|
||||||
|
msgstr "Bannir l'utilisateur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:175
|
||||||
|
msgid "Channel"
|
||||||
|
msgstr "Canal"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:177
|
||||||
|
msgid "Channel ID"
|
||||||
|
msgstr "ID du canal"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:179
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "Se connecter"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:182
|
||||||
|
msgid "Channel description"
|
||||||
|
msgstr "Description du canal"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:229
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Supprimer"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:265
|
||||||
|
msgid "Server Info"
|
||||||
|
msgstr "Information du serveur"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:266
|
||||||
|
msgid "Registration"
|
||||||
|
msgstr "Enregistrement"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:275
|
||||||
|
msgid "Administration"
|
||||||
|
msgstr "Administration"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:282
|
||||||
|
msgid "User List"
|
||||||
|
msgstr "Liste des utilisateurs"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:286
|
||||||
|
msgid "name"
|
||||||
|
msgstr "nom"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:306
|
||||||
|
msgid "Change password"
|
||||||
|
msgstr "Changer le mot de passe"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:319
|
||||||
|
msgid "Add"
|
||||||
|
msgstr "Ajouter"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:331
|
||||||
|
msgid "Save"
|
||||||
|
msgstr "Sauvegarder"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:357
|
||||||
|
msgid "Resync with Murmur"
|
||||||
|
msgstr "Synchroniser avec Murmur"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:15
|
||||||
|
msgid "Suppressed"
|
||||||
|
msgstr "Muet"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:27
|
||||||
|
msgid "has a User Comment set"
|
||||||
|
msgstr "a un commentaire"
|
||||||
|
|
||||||
|
#~ msgid "comment"
|
||||||
|
#~ msgstr "commentaire"
|
||||||
|
|
||||||
|
#~ msgid "hash"
|
||||||
|
#~ msgstr "hash"
|
||||||
BIN
mumble/locale/it/LC_MESSAGES/django.mo
Normal file
BIN
mumble/locale/it/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
573
mumble/locale/it/LC_MESSAGES/django.po
Normal file
573
mumble/locale/it/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,573 @@
|
|||||||
|
# Italian translation file for Mumble-Django.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009, satinez <info@satinez.net>
|
||||||
|
#
|
||||||
|
# Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This package is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Mumble-Django v0.8\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2010-03-14 18:23+0100\n"
|
||||||
|
"PO-Revision-Date: 2010-03-14 13:51\n"
|
||||||
|
"Last-Translator: <admin@admin.de>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Translated-Using: django-rosetta 0.5.3\n"
|
||||||
|
|
||||||
|
#: admin.py:34 admin.py:51
|
||||||
|
msgid "Master is running"
|
||||||
|
msgstr "Il Master è in esecuzione"
|
||||||
|
|
||||||
|
#: admin.py:59 models.py:162 templates/mumble/mumble.html:28
|
||||||
|
msgid "Server Address"
|
||||||
|
msgstr "Indirizzo del Server"
|
||||||
|
|
||||||
|
#: admin.py:66 models.py:165
|
||||||
|
msgid "Server Port"
|
||||||
|
msgstr "Port del Server"
|
||||||
|
|
||||||
|
#: admin.py:71
|
||||||
|
msgid "Instance is running"
|
||||||
|
msgstr "Istanza è in esecuzione"
|
||||||
|
|
||||||
|
#: admin.py:81
|
||||||
|
msgid "Registered users"
|
||||||
|
msgstr "Utenti registrati"
|
||||||
|
|
||||||
|
#: admin.py:91
|
||||||
|
msgid "Online users"
|
||||||
|
msgstr "Utenti online"
|
||||||
|
|
||||||
|
#: admin.py:101
|
||||||
|
msgid "Channel count"
|
||||||
|
msgstr "Quantità canali"
|
||||||
|
|
||||||
|
#: admin.py:108
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr "Sì"
|
||||||
|
|
||||||
|
#: admin.py:110
|
||||||
|
msgid "No"
|
||||||
|
msgstr "No"
|
||||||
|
|
||||||
|
#: admin.py:114
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "Publico"
|
||||||
|
|
||||||
|
#: admin.py:132 models.py:631 templates/mumble/mumble.html:223
|
||||||
|
msgid "Admin on root channel"
|
||||||
|
msgstr "Admin nel canale root"
|
||||||
|
|
||||||
|
#: forms.py:83
|
||||||
|
msgid "Password required to join. Leave empty for public servers."
|
||||||
|
msgstr "Password richiesta per entrare. Lasciare vuoto per i server pubblici."
|
||||||
|
|
||||||
|
#: forms.py:86
|
||||||
|
msgid "If on, IP adresses of the clients are not logged."
|
||||||
|
msgstr "Se è acceso, indirizzi IP dei clienti non vengono registrati."
|
||||||
|
|
||||||
|
#: forms.py:142
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Port number %(portno)d is not within the allowed range %(minrange)d - %"
|
||||||
|
"(maxrange)d"
|
||||||
|
msgstr ""
|
||||||
|
"Il Port %(portno)d non è consentito nel range %(minrange)d - %(maxrange)d"
|
||||||
|
|
||||||
|
#: forms.py:152
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Default config"
|
||||||
|
msgstr "Canale default"
|
||||||
|
|
||||||
|
#: forms.py:165 templates/mumble/offline.html:12
|
||||||
|
msgid "This server is currently offline."
|
||||||
|
msgstr "Al momento questo server è offline."
|
||||||
|
|
||||||
|
#: forms.py:190
|
||||||
|
msgid "That name is forbidden by the server."
|
||||||
|
msgstr "Questo nome è proibito dal server."
|
||||||
|
|
||||||
|
#: forms.py:193 models.py:587
|
||||||
|
msgid "Another player already registered that name."
|
||||||
|
msgstr "Un altro giocatore è stato già sotto questo nome registrato."
|
||||||
|
|
||||||
|
#: forms.py:201 forms.py:307 models.py:589
|
||||||
|
msgid "Cannot register player without a password!"
|
||||||
|
msgstr "Non è possibile registrarsi senza una password!"
|
||||||
|
|
||||||
|
#: forms.py:213 models.py:179
|
||||||
|
msgid "Server Password"
|
||||||
|
msgstr "Server password"
|
||||||
|
|
||||||
|
#: forms.py:214
|
||||||
|
msgid ""
|
||||||
|
"This server is private and protected mode is active. Please enter the server "
|
||||||
|
"password."
|
||||||
|
msgstr ""
|
||||||
|
"Server privato, la protezione di registro è attiva. Digiti prego la password "
|
||||||
|
"d'accesso."
|
||||||
|
|
||||||
|
#: forms.py:222 forms.py:274
|
||||||
|
msgid "The password you entered is incorrect."
|
||||||
|
msgstr "La password d'accesso non è corretta."
|
||||||
|
|
||||||
|
#: forms.py:237
|
||||||
|
msgid "Link account"
|
||||||
|
msgstr "Link il conto"
|
||||||
|
|
||||||
|
#: forms.py:238
|
||||||
|
msgid ""
|
||||||
|
"The account already exists and belongs to me, just link it instead of "
|
||||||
|
"creating."
|
||||||
|
msgstr "L'account esiste già."
|
||||||
|
|
||||||
|
#: forms.py:255
|
||||||
|
msgid "No such user found."
|
||||||
|
msgstr "Nessun utente trovato."
|
||||||
|
|
||||||
|
#: forms.py:290
|
||||||
|
msgid "That account belongs to someone else."
|
||||||
|
msgstr "L'account appartiene a qualcun altro."
|
||||||
|
|
||||||
|
#: forms.py:293
|
||||||
|
msgid "Linking Admin accounts is not allowed."
|
||||||
|
msgstr "Collegare account Amministratore non è permesso."
|
||||||
|
|
||||||
|
#: forms.py:322 templates/mumble/mumble.html:65
|
||||||
|
#: templates/mumble/mumble.html.py:148 templates/mumble/mumble.html:278
|
||||||
|
msgid "User Texture"
|
||||||
|
msgstr "Immagine del Utente"
|
||||||
|
|
||||||
|
#: models.py:63
|
||||||
|
msgid "DBus or ICE base"
|
||||||
|
msgstr "DBus- o ICE-base"
|
||||||
|
|
||||||
|
#: models.py:64
|
||||||
|
msgid ""
|
||||||
|
"Examples: 'net.sourceforge.mumble.murmur' for DBus or 'Meta:tcp -h 127.0.0.1 "
|
||||||
|
"-p 6502' for Ice."
|
||||||
|
msgstr ""
|
||||||
|
"Esempi: 'net.sourceforge.mumble.murmur' per DBus o 'Meta: tcp-h 127.0.0.1-p "
|
||||||
|
"6502' per ICE."
|
||||||
|
|
||||||
|
#: models.py:65
|
||||||
|
msgid "Ice Secret"
|
||||||
|
msgstr "ICE Secret"
|
||||||
|
|
||||||
|
#: models.py:68 models.py:159
|
||||||
|
msgid "Mumble Server"
|
||||||
|
msgstr "Mumble Server"
|
||||||
|
|
||||||
|
#: models.py:69
|
||||||
|
msgid "Mumble Servers"
|
||||||
|
msgstr "Mumble Servers"
|
||||||
|
|
||||||
|
#: models.py:160
|
||||||
|
msgid "Server Name"
|
||||||
|
msgstr "Nome del Server"
|
||||||
|
|
||||||
|
#: models.py:161
|
||||||
|
msgid "Server ID"
|
||||||
|
msgstr "Server-ID"
|
||||||
|
|
||||||
|
#: models.py:163
|
||||||
|
msgid ""
|
||||||
|
"Hostname or IP address to bind to. You should use a hostname here, because "
|
||||||
|
"it will appear on the global server list."
|
||||||
|
msgstr ""
|
||||||
|
"Hostname o l'indirizzo IP da associare. Si dovrebbe usare un hostname qui, "
|
||||||
|
"perché sarà visualizzato nella lista globale dei server."
|
||||||
|
|
||||||
|
#: models.py:166
|
||||||
|
msgid "Port number to bind to. Leave empty to auto assign one."
|
||||||
|
msgstr "Numero del Port da associare. Lasciare vuoto per auto-assegnare uno."
|
||||||
|
|
||||||
|
#: models.py:167
|
||||||
|
msgid "Server Display Address"
|
||||||
|
msgstr "l'indirizzo Server display"
|
||||||
|
|
||||||
|
#: models.py:168
|
||||||
|
msgid ""
|
||||||
|
"This field is only relevant if you are located behind a NAT, and names the "
|
||||||
|
"Hostname or IP address to use in the Channel Viewer and for the global "
|
||||||
|
"server list registration. If not given, the addr and port fields are used. "
|
||||||
|
"If display and bind ports are equal, you can omit it here."
|
||||||
|
msgstr ""
|
||||||
|
"Questo campo è rilevante solo se si trova dietro un NAT, nomi e il Hostname "
|
||||||
|
"o indirizzi IP da utilizzare nel canale Viewer e per la lista globale dei "
|
||||||
|
"Server. \n"
|
||||||
|
"\n"
|
||||||
|
"Se il Port del display e il Port del bind sono uguali, è possibile omettere "
|
||||||
|
"qui."
|
||||||
|
|
||||||
|
#: models.py:174
|
||||||
|
msgid "Superuser Password"
|
||||||
|
msgstr "SuperUser password"
|
||||||
|
|
||||||
|
#: models.py:177
|
||||||
|
msgid "Website URL"
|
||||||
|
msgstr "URL del sito web"
|
||||||
|
|
||||||
|
#: models.py:178
|
||||||
|
msgid "Welcome Message"
|
||||||
|
msgstr "Messaggio di benvenuto"
|
||||||
|
|
||||||
|
#: models.py:180
|
||||||
|
msgid "Max. Users"
|
||||||
|
msgstr "Max. Utenti"
|
||||||
|
|
||||||
|
#: models.py:181
|
||||||
|
msgid "Bandwidth [Bps]"
|
||||||
|
msgstr "larghezza di banda [Bps]"
|
||||||
|
|
||||||
|
#: models.py:182
|
||||||
|
msgid "SSL Certificate"
|
||||||
|
msgstr "SSL-Certificato"
|
||||||
|
|
||||||
|
#: models.py:183
|
||||||
|
msgid "SSL Key"
|
||||||
|
msgstr "SSL-Chiave"
|
||||||
|
|
||||||
|
#: models.py:184
|
||||||
|
msgid "Player name regex"
|
||||||
|
msgstr "Regex per giocatori"
|
||||||
|
|
||||||
|
#: models.py:185
|
||||||
|
msgid "Channel name regex"
|
||||||
|
msgstr "Regex per Canali"
|
||||||
|
|
||||||
|
#: models.py:186
|
||||||
|
msgid "Default channel"
|
||||||
|
msgstr "Canale default"
|
||||||
|
|
||||||
|
#: models.py:187
|
||||||
|
msgid "Timeout"
|
||||||
|
msgstr "Timeout"
|
||||||
|
|
||||||
|
#: models.py:189
|
||||||
|
msgid "IP Obfuscation"
|
||||||
|
msgstr "Anonimizza indirizzo IP"
|
||||||
|
|
||||||
|
#: models.py:190
|
||||||
|
msgid "Require Certificate"
|
||||||
|
msgstr "Serve il Certificate"
|
||||||
|
|
||||||
|
#: models.py:191
|
||||||
|
msgid "Maximum length of text messages"
|
||||||
|
msgstr "La lunghezza massima dei messaggi"
|
||||||
|
|
||||||
|
#: models.py:192
|
||||||
|
msgid "Allow HTML to be used in messages"
|
||||||
|
msgstr "Permettere HTML nei messaggi"
|
||||||
|
|
||||||
|
#: models.py:193
|
||||||
|
msgid "Publish this server via Bonjour"
|
||||||
|
msgstr "Pubblica questo server tramite Bonjour"
|
||||||
|
|
||||||
|
#: models.py:194
|
||||||
|
msgid "Boot Server when Murmur starts"
|
||||||
|
msgstr "Boot Server quando si avvia Murmur"
|
||||||
|
|
||||||
|
#: models.py:212 models.py:213
|
||||||
|
msgid "Boot Server"
|
||||||
|
msgstr "Boot Server"
|
||||||
|
|
||||||
|
#: models.py:217 models.py:550
|
||||||
|
msgid "Server instance"
|
||||||
|
msgstr "Istanza di server"
|
||||||
|
|
||||||
|
#: models.py:218
|
||||||
|
msgid "Server instances"
|
||||||
|
msgstr "Istanze di server"
|
||||||
|
|
||||||
|
#: models.py:510 models.py:690
|
||||||
|
msgid "This field must not be updated once the record has been saved."
|
||||||
|
msgstr ""
|
||||||
|
"Questo campo non si puo cambiare, dopo che é stato salvato per la prima "
|
||||||
|
"volta."
|
||||||
|
|
||||||
|
#: models.py:547
|
||||||
|
msgid "Mumble player_id"
|
||||||
|
msgstr "ID del giocatore in Murmur"
|
||||||
|
|
||||||
|
#: models.py:548
|
||||||
|
msgid "User name and Login"
|
||||||
|
msgstr "Nome Utente e Login"
|
||||||
|
|
||||||
|
#: models.py:549
|
||||||
|
msgid "Login password"
|
||||||
|
msgstr "Login password"
|
||||||
|
|
||||||
|
#: models.py:551 templates/mumble/mumble.html:293
|
||||||
|
msgid "Account owner"
|
||||||
|
msgstr "Proprietario del account"
|
||||||
|
|
||||||
|
#: models.py:553
|
||||||
|
msgid "The user's comment."
|
||||||
|
msgstr "Commento dell'utente."
|
||||||
|
|
||||||
|
#: models.py:554
|
||||||
|
msgid "The user's hash."
|
||||||
|
msgstr "Hash dell'utente."
|
||||||
|
|
||||||
|
#: models.py:558
|
||||||
|
msgid "User account"
|
||||||
|
msgstr "Account del utente"
|
||||||
|
|
||||||
|
#: models.py:559
|
||||||
|
msgid "User accounts"
|
||||||
|
msgstr "Accounts degli utenti"
|
||||||
|
|
||||||
|
#: models.py:566
|
||||||
|
#, python-format
|
||||||
|
msgid "Mumble user %(mu)s on %(srv)s owned by Django user %(du)s"
|
||||||
|
msgstr "Account %(mu)s su %(srv)s con Utente %(du)s"
|
||||||
|
|
||||||
|
#: templates/mumble/list.html:20
|
||||||
|
msgid "No server instances have been configured yet."
|
||||||
|
msgstr "Nessune istanze del server sono stati configurati."
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:16
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <b>Hint:</b><br />\n"
|
||||||
|
" This area is used to display additional information for each channel "
|
||||||
|
"and player, but requires JavaScript to be\n"
|
||||||
|
" displayed correctly. You will not see the detail pages, but you can "
|
||||||
|
"use all links and forms\n"
|
||||||
|
" that are displayed.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <b>Nota</b><br />\n"
|
||||||
|
" Questo settore è utilizzato per visualizzare informazioni aggiuntive "
|
||||||
|
"per ogni canale e giocatore, ma richiede JavaScript per essere\n"
|
||||||
|
" visualizzato correttamente. Non vedi il dettaglio delle pagine, ma "
|
||||||
|
"puoi utilizzare tutti i link e le forme\n"
|
||||||
|
" che vengono visualizzati.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:31
|
||||||
|
msgid "Website"
|
||||||
|
msgstr "Sito web"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:33
|
||||||
|
msgid "Server version"
|
||||||
|
msgstr "Versione del Server"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:34
|
||||||
|
msgid "Minimal view"
|
||||||
|
msgstr "La visualizzazione minimale"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:37
|
||||||
|
msgid "Welcome message"
|
||||||
|
msgstr "Messaggio di benvenuto"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:43
|
||||||
|
msgid "Server registration"
|
||||||
|
msgstr "Registrazione Server"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:46
|
||||||
|
msgid "You are registered on this server"
|
||||||
|
msgstr "Sei registrato su questo Server "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:48
|
||||||
|
msgid "You do not have an account on this server"
|
||||||
|
msgstr "Non sei registrato su questo Server "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:57
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <p>You need to be <a href=\"%(login_url)s\">logged in</a> to be able "
|
||||||
|
"to register an account on this Mumble server.</p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <p>Devi avere un <a href=\"%(login_url)s\">login</a> per registrarti "
|
||||||
|
"su questo mumble server</p>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:67
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Sorry, due to a bug in Murmur 1.2.2, displaying and setting the "
|
||||||
|
"Texture is disabled.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"Scusa, a causa di un bug in Murmur 1.2.2, la visualizzazione e "
|
||||||
|
"l'impostazione è disattivato."
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:72
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" You can upload an image that you would like to use as your user "
|
||||||
|
"texture here.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"È possibile caricare l'immagine che si desidera utilizzare come texture "
|
||||||
|
"utente."
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:77
|
||||||
|
msgid "Your current texture is"
|
||||||
|
msgstr "Il tuo attuale immagine"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:80
|
||||||
|
msgid "You don't currently have a texture set"
|
||||||
|
msgstr "Al momento non hai dei texture set"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:84
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Hint: The texture image <b>needs</b> to be 600x60 in size. If "
|
||||||
|
"you upload an image with\n"
|
||||||
|
" a different size, it will be resized accordingly.<br />\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"Nota! La immagine <b>deve avere</b> le dimensioni di 600x60 pixel. Se hai "
|
||||||
|
"un'immagine con una dimensione diversa, si aggiusta automaticamente. "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:103
|
||||||
|
msgid "Server administration"
|
||||||
|
msgstr "Amministrazione del Server"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:117
|
||||||
|
msgid "Player"
|
||||||
|
msgstr "Giocatore"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:119
|
||||||
|
msgid "Online since"
|
||||||
|
msgstr "Online da"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:120 templates/mumble/player.html:9
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "Authenticated"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:121 templates/mumble/mumble.html.py:136
|
||||||
|
msgid "Admin"
|
||||||
|
msgstr "Amministratore"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:122 templates/mumble/player.html:12
|
||||||
|
msgid "Muted"
|
||||||
|
msgstr "Muto"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:123 templates/mumble/player.html:18
|
||||||
|
msgid "Deafened"
|
||||||
|
msgstr "Sordo"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:124 templates/mumble/player.html:21
|
||||||
|
msgid "Muted by self"
|
||||||
|
msgstr "Mutato solo"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:125 templates/mumble/player.html:24
|
||||||
|
msgid "Deafened by self"
|
||||||
|
msgstr "Rintronarsi"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:127
|
||||||
|
msgid "IP Address"
|
||||||
|
msgstr "Indirizzo IP del Server"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:131
|
||||||
|
msgid "User"
|
||||||
|
msgstr "Utente"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:134
|
||||||
|
msgid "Full Name"
|
||||||
|
msgstr "Nome"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:137
|
||||||
|
msgid "Sign-up date"
|
||||||
|
msgstr "Data di registrazione"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:142
|
||||||
|
msgid "User Comment"
|
||||||
|
msgstr "User Comment"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:154 templates/mumble/mumble.html.py:168
|
||||||
|
msgid "Kick user"
|
||||||
|
msgstr "Kick utente"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:160
|
||||||
|
msgid "Reason"
|
||||||
|
msgstr "Diritto"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:165
|
||||||
|
msgid "Ban user"
|
||||||
|
msgstr "Ban utenti"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:175
|
||||||
|
msgid "Channel"
|
||||||
|
msgstr "Canale"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:177
|
||||||
|
msgid "Channel ID"
|
||||||
|
msgstr "ID del Canale"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:179
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "Connect"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:182
|
||||||
|
msgid "Channel description"
|
||||||
|
msgstr "descrizione del canale"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:229
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Cancellare"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:265
|
||||||
|
msgid "Server Info"
|
||||||
|
msgstr "Informazioni del Server"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:266
|
||||||
|
msgid "Registration"
|
||||||
|
msgstr "Registrazione"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:275
|
||||||
|
msgid "Administration"
|
||||||
|
msgstr "Amministrazione"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:282
|
||||||
|
msgid "User List"
|
||||||
|
msgstr "Lista dei utenti"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:286
|
||||||
|
msgid "name"
|
||||||
|
msgstr "Nome"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:306
|
||||||
|
msgid "Change password"
|
||||||
|
msgstr "Cambia la password"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:319
|
||||||
|
msgid "Add"
|
||||||
|
msgstr "Aggiungere"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:331
|
||||||
|
msgid "Save"
|
||||||
|
msgstr "Salva"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:357
|
||||||
|
msgid "Resync with Murmur"
|
||||||
|
msgstr "Sincronizza con Murmur"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:15
|
||||||
|
msgid "Suppressed"
|
||||||
|
msgstr "Soppresso"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:27
|
||||||
|
msgid "has a User Comment set"
|
||||||
|
msgstr "Ha un commento del Utente set"
|
||||||
BIN
mumble/locale/ja/LC_MESSAGES/django.mo
Normal file
BIN
mumble/locale/ja/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
543
mumble/locale/ja/LC_MESSAGES/django.po
Normal file
543
mumble/locale/ja/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,543 @@
|
|||||||
|
# Japanese translation file for Mumble-Django.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009, withgod <noname@withgod.jp>
|
||||||
|
# Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
#
|
||||||
|
# Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This package is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Mumble-Django v0.8\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2010-03-14 18:23+0100\n"
|
||||||
|
"PO-Revision-Date: 2010-03-15 21:47\n"
|
||||||
|
"Last-Translator: <nocontents@gmail.com>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Translated-Using: django-rosetta 0.5.2\n"
|
||||||
|
|
||||||
|
#: admin.py:34 admin.py:51
|
||||||
|
msgid "Master is running"
|
||||||
|
msgstr "マスター実行中"
|
||||||
|
|
||||||
|
#: admin.py:59 models.py:162 templates/mumble/mumble.html:28
|
||||||
|
msgid "Server Address"
|
||||||
|
msgstr "サーバアドレス"
|
||||||
|
|
||||||
|
#: admin.py:66 models.py:165
|
||||||
|
msgid "Server Port"
|
||||||
|
msgstr "サーバポート"
|
||||||
|
|
||||||
|
#: admin.py:71
|
||||||
|
msgid "Instance is running"
|
||||||
|
msgstr "インスタンス実行中"
|
||||||
|
|
||||||
|
#: admin.py:81
|
||||||
|
msgid "Registered users"
|
||||||
|
msgstr "ユーザ登録"
|
||||||
|
|
||||||
|
#: admin.py:91
|
||||||
|
msgid "Online users"
|
||||||
|
msgstr "オンラインのユーザ"
|
||||||
|
|
||||||
|
#: admin.py:101
|
||||||
|
msgid "Channel count"
|
||||||
|
msgstr "チャンネル数"
|
||||||
|
|
||||||
|
#: admin.py:108
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr "はい"
|
||||||
|
|
||||||
|
#: admin.py:110
|
||||||
|
msgid "No"
|
||||||
|
msgstr "いいえ"
|
||||||
|
|
||||||
|
#: admin.py:114
|
||||||
|
msgid "Public"
|
||||||
|
msgstr "公開"
|
||||||
|
|
||||||
|
#: admin.py:132 models.py:631 templates/mumble/mumble.html:223
|
||||||
|
msgid "Admin on root channel"
|
||||||
|
msgstr "管理者権限を与える"
|
||||||
|
|
||||||
|
#: forms.py:83
|
||||||
|
msgid "Password required to join. Leave empty for public servers."
|
||||||
|
msgstr "サーバに参加するにはパスワードが必要。空白にすると公開サーバになります。"
|
||||||
|
|
||||||
|
#: forms.py:86
|
||||||
|
msgid "If on, IP adresses of the clients are not logged."
|
||||||
|
msgstr "If on, IP adresses of the clients are not logged."
|
||||||
|
|
||||||
|
#: forms.py:142
|
||||||
|
#, python-format
|
||||||
|
msgid "Port number %(portno)d is not within the allowed range %(minrange)d - %(maxrange)d"
|
||||||
|
msgstr "ポート番号 %(portno)d は許可されていません、次の範囲が許可されています %(minrange)d - %(maxrange)d"
|
||||||
|
|
||||||
|
#: forms.py:152
|
||||||
|
msgid "Default config"
|
||||||
|
msgstr "デフォルトコンフィグ"
|
||||||
|
|
||||||
|
#: forms.py:165 templates/mumble/offline.html:12
|
||||||
|
msgid "This server is currently offline."
|
||||||
|
msgstr "このサーバーは現在停止中です。"
|
||||||
|
|
||||||
|
#: forms.py:190
|
||||||
|
msgid "That name is forbidden by the server."
|
||||||
|
msgstr "その名前はサーバによって不可視にされています"
|
||||||
|
|
||||||
|
#: forms.py:193 models.py:587
|
||||||
|
msgid "Another player already registered that name."
|
||||||
|
msgstr "その名前は既に使われています."
|
||||||
|
|
||||||
|
#: forms.py:201 forms.py:307 models.py:589
|
||||||
|
msgid "Cannot register player without a password!"
|
||||||
|
msgstr "パスワードは必須です"
|
||||||
|
|
||||||
|
#: forms.py:213 models.py:179
|
||||||
|
msgid "Server Password"
|
||||||
|
msgstr "サーバパスワード"
|
||||||
|
|
||||||
|
#: forms.py:214
|
||||||
|
msgid "This server is private and protected mode is active. Please enter the server password."
|
||||||
|
msgstr "本サーバをプライベート及びプロテクトモードで利用する場合は、パスワードを入力してください。"
|
||||||
|
|
||||||
|
#: forms.py:222 forms.py:274
|
||||||
|
msgid "The password you entered is incorrect."
|
||||||
|
msgstr "パスワードが不正です。"
|
||||||
|
|
||||||
|
#: forms.py:237
|
||||||
|
msgid "Link account"
|
||||||
|
msgstr "アカウントリンク"
|
||||||
|
|
||||||
|
#: forms.py:238
|
||||||
|
msgid "The account already exists and belongs to me, just link it instead of creating."
|
||||||
|
msgstr "アカウントは既に存在していて、自分にリンクしています。リンクの変わりに作成して下さい。"
|
||||||
|
|
||||||
|
#: forms.py:255
|
||||||
|
msgid "No such user found."
|
||||||
|
msgstr "ユーザが見つかりません"
|
||||||
|
|
||||||
|
#: forms.py:290
|
||||||
|
msgid "That account belongs to someone else."
|
||||||
|
msgstr "このアカウントは既に他のアカウントにリンクされています。"
|
||||||
|
|
||||||
|
#: forms.py:293
|
||||||
|
msgid "Linking Admin accounts is not allowed."
|
||||||
|
msgstr "管理者アカウントへのリンクは許可されていません"
|
||||||
|
|
||||||
|
#: forms.py:322 templates/mumble/mumble.html:65
|
||||||
|
#: templates/mumble/mumble.html.py:148 templates/mumble/mumble.html:278
|
||||||
|
msgid "User Texture"
|
||||||
|
msgstr "ユーザ画像"
|
||||||
|
|
||||||
|
#: models.py:63
|
||||||
|
msgid "DBus or ICE base"
|
||||||
|
msgstr "DBusもしくはICE"
|
||||||
|
|
||||||
|
#: models.py:64
|
||||||
|
msgid "Examples: 'net.sourceforge.mumble.murmur' for DBus or 'Meta:tcp -h 127.0.0.1 -p 6502' for Ice."
|
||||||
|
msgstr "例: DBusなら'net.sourceforge.mumble.murmur' Iceなら'Meta:tcp -h 127.0.0.1 -p 6502'"
|
||||||
|
|
||||||
|
#: models.py:65
|
||||||
|
msgid "Ice Secret"
|
||||||
|
msgstr "Ice Secret"
|
||||||
|
|
||||||
|
#: models.py:68 models.py:159
|
||||||
|
msgid "Mumble Server"
|
||||||
|
msgstr "マンブルユーザID"
|
||||||
|
|
||||||
|
#: models.py:69
|
||||||
|
msgid "Mumble Servers"
|
||||||
|
msgstr "マンブルユーザID"
|
||||||
|
|
||||||
|
#: models.py:160
|
||||||
|
msgid "Server Name"
|
||||||
|
msgstr "サーバ名"
|
||||||
|
|
||||||
|
#: models.py:161
|
||||||
|
msgid "Server ID"
|
||||||
|
msgstr "サーバID"
|
||||||
|
|
||||||
|
#: models.py:163
|
||||||
|
msgid "Hostname or IP address to bind to. You should use a hostname here, because it will appear on the global server list."
|
||||||
|
msgstr "利用するホスト名もしくはIPアドレス. ホスト名も利用することが出来ます。 サーバ一覧に表示されます。."
|
||||||
|
|
||||||
|
#: models.py:166
|
||||||
|
msgid "Port number to bind to. Leave empty to auto assign one."
|
||||||
|
msgstr "利用するポート番号を指定します、未指定の場合空いているポートを利用します。"
|
||||||
|
|
||||||
|
#: models.py:167
|
||||||
|
msgid "Server Display Address"
|
||||||
|
msgstr "サーバアドレス"
|
||||||
|
|
||||||
|
#: models.py:168
|
||||||
|
msgid "This field is only relevant if you are located behind a NAT, and names the Hostname or IP address to use in the Channel Viewer and for the global server list registration. If not given, the addr and port fields are used. If display and bind ports are equal, you can omit it here."
|
||||||
|
msgstr "本項目は、NATの内側等にあるサーバに適切です。ホスト名もしくはIPアドレスを、チャンネルビューアや、サーバリスト登録に使います。 "
|
||||||
|
|
||||||
|
#: models.py:174
|
||||||
|
msgid "Superuser Password"
|
||||||
|
msgstr "管理者パスワード"
|
||||||
|
|
||||||
|
#: models.py:177
|
||||||
|
msgid "Website URL"
|
||||||
|
msgstr "ウェブサイトのURL"
|
||||||
|
|
||||||
|
#: models.py:178
|
||||||
|
msgid "Welcome Message"
|
||||||
|
msgstr "サーバメッセージ"
|
||||||
|
|
||||||
|
#: models.py:180
|
||||||
|
msgid "Max. Users"
|
||||||
|
msgstr "最大ユーザ数"
|
||||||
|
|
||||||
|
#: models.py:181
|
||||||
|
msgid "Bandwidth [Bps]"
|
||||||
|
msgstr "帯域制限 [Bps]"
|
||||||
|
|
||||||
|
#: models.py:182
|
||||||
|
msgid "SSL Certificate"
|
||||||
|
msgstr "SSL認証"
|
||||||
|
|
||||||
|
#: models.py:183
|
||||||
|
msgid "SSL Key"
|
||||||
|
msgstr "SSL認証鍵"
|
||||||
|
|
||||||
|
#: models.py:184
|
||||||
|
msgid "Player name regex"
|
||||||
|
msgstr "ユーザ名の正規表現制限"
|
||||||
|
|
||||||
|
#: models.py:185
|
||||||
|
msgid "Channel name regex"
|
||||||
|
msgstr "チャンネル名の正規表現制限"
|
||||||
|
|
||||||
|
#: models.py:186
|
||||||
|
msgid "Default channel"
|
||||||
|
msgstr "デフォルトチャンネル"
|
||||||
|
|
||||||
|
#: models.py:187
|
||||||
|
msgid "Timeout"
|
||||||
|
msgstr "タイムアウト"
|
||||||
|
|
||||||
|
#: models.py:189
|
||||||
|
msgid "IP Obfuscation"
|
||||||
|
msgstr "IPアドレス隠蔽"
|
||||||
|
|
||||||
|
#: models.py:190
|
||||||
|
msgid "Require Certificate"
|
||||||
|
msgstr "SSL認証"
|
||||||
|
|
||||||
|
#: models.py:191
|
||||||
|
msgid "Maximum length of text messages"
|
||||||
|
msgstr "テキストメッセージの最大文字数"
|
||||||
|
|
||||||
|
#: models.py:192
|
||||||
|
msgid "Allow HTML to be used in messages"
|
||||||
|
msgstr "HTMLメッセージを許可する"
|
||||||
|
|
||||||
|
#: models.py:193
|
||||||
|
msgid "Publish this server via Bonjour"
|
||||||
|
msgstr "Bonjourでこのサーバを公開する"
|
||||||
|
|
||||||
|
#: models.py:194
|
||||||
|
msgid "Boot Server when Murmur starts"
|
||||||
|
msgstr "Murmur起動時にサーバを起動します"
|
||||||
|
|
||||||
|
#: models.py:212 models.py:213
|
||||||
|
msgid "Boot Server"
|
||||||
|
msgstr "サーバ起動"
|
||||||
|
|
||||||
|
#: models.py:217 models.py:550
|
||||||
|
msgid "Server instance"
|
||||||
|
msgstr "サーバインスタンス"
|
||||||
|
|
||||||
|
#: models.py:218
|
||||||
|
msgid "Server instances"
|
||||||
|
msgstr "全サーバインスタンス"
|
||||||
|
|
||||||
|
#: models.py:510 models.py:690
|
||||||
|
msgid "This field must not be updated once the record has been saved."
|
||||||
|
msgstr "この項目はアップデート出来ません."
|
||||||
|
|
||||||
|
#: models.py:547
|
||||||
|
msgid "Mumble player_id"
|
||||||
|
msgstr "マンブルユーザID"
|
||||||
|
|
||||||
|
#: models.py:548
|
||||||
|
msgid "User name and Login"
|
||||||
|
msgstr "ユーザID"
|
||||||
|
|
||||||
|
#: models.py:549
|
||||||
|
msgid "Login password"
|
||||||
|
msgstr "パスワード"
|
||||||
|
|
||||||
|
#: models.py:551 templates/mumble/mumble.html:293
|
||||||
|
msgid "Account owner"
|
||||||
|
msgstr "アカウントの所有者"
|
||||||
|
|
||||||
|
#: models.py:553
|
||||||
|
msgid "The user's comment."
|
||||||
|
msgstr "ユーザコメント"
|
||||||
|
|
||||||
|
#: models.py:554
|
||||||
|
msgid "The user's hash."
|
||||||
|
msgstr "ユーザのハッシュ"
|
||||||
|
|
||||||
|
#: models.py:558
|
||||||
|
msgid "User account"
|
||||||
|
msgstr "ユーザアカウント"
|
||||||
|
|
||||||
|
#: models.py:559
|
||||||
|
msgid "User accounts"
|
||||||
|
msgstr "全ユーザアカウント"
|
||||||
|
|
||||||
|
#: models.py:566
|
||||||
|
#, python-format
|
||||||
|
msgid "Mumble user %(mu)s on %(srv)s owned by Django user %(du)s"
|
||||||
|
msgstr "マンブルユーザ %(mu)s は %(srv)s のDjangoユーザ %(du)s です"
|
||||||
|
|
||||||
|
#: templates/mumble/list.html:20
|
||||||
|
msgid "No server instances have been configured yet."
|
||||||
|
msgstr "サーバ設定が存在しません"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:16
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <b>Hint:</b><br />\n"
|
||||||
|
" This area is used to display additional information for each channel and player, but requires JavaScript to be\n"
|
||||||
|
" displayed correctly. You will not see the detail pages, but you can use all links and forms\n"
|
||||||
|
" that are displayed.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <b>ヒント:</b><br />\n"
|
||||||
|
" このエリアはチャンネル情報をやプレーヤー情報を表示するのにjavascriptが必要です\n"
|
||||||
|
" javascriptをオンにしないと、すべての情報を閲覧することは出来ません。 "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:31
|
||||||
|
msgid "Website"
|
||||||
|
msgstr "ウェブサイト"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:33
|
||||||
|
msgid "Server version"
|
||||||
|
msgstr "サーババージョン"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:34
|
||||||
|
msgid "Minimal view"
|
||||||
|
msgstr "縮小表示"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:37
|
||||||
|
msgid "Welcome message"
|
||||||
|
msgstr "サーバメッセージ"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:43
|
||||||
|
msgid "Server registration"
|
||||||
|
msgstr "サーバ登録"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:46
|
||||||
|
msgid "You are registered on this server"
|
||||||
|
msgstr "アカウントは既に登録されています"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:48
|
||||||
|
msgid "You do not have an account on this server"
|
||||||
|
msgstr "アカウントは登録されていません"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:57
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" <p>You need to be <a href=\"%(login_url)s\">logged in</a> to be able to register an account on this Mumble server.</p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" <p><a href=\"%(login_url)s\">ログイン</a>してから ユーザをこのMumbleサーバに登録する必要があります</p>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:67
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Sorry, due to a bug in Murmur 1.2.2, displaying and setting the Texture is disabled.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"1.2.2 ではユーザ画像の表示がバグってるため、ユーザ画像表示は無効です。"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:72
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" You can upload an image that you would like to use as your user texture here.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"任意のユーザ画像をここから登録することがきます。"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:77
|
||||||
|
msgid "Your current texture is"
|
||||||
|
msgstr "現在の画像"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:80
|
||||||
|
msgid "You don't currently have a texture set"
|
||||||
|
msgstr "貴方は画像を持っていません"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:84
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Hint: The texture image <b>needs</b> to be 600x60 in size. If you upload an image with\n"
|
||||||
|
" a different size, it will be resized accordingly.<br />\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"ヒント: 画像は600x60である<b>必要</b>があります。そのサイズを超えたり収まらない 場合は、リサイズが行われます。 <br />\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:103
|
||||||
|
msgid "Server administration"
|
||||||
|
msgstr "サーバ管理"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:117
|
||||||
|
msgid "Player"
|
||||||
|
msgstr "プレーヤ"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:119
|
||||||
|
msgid "Online since"
|
||||||
|
msgstr "接続開始時間"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:120 templates/mumble/player.html:9
|
||||||
|
msgid "Authenticated"
|
||||||
|
msgstr "登録済み"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:121 templates/mumble/mumble.html.py:136
|
||||||
|
msgid "Admin"
|
||||||
|
msgstr "管理者"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:122 templates/mumble/player.html:12
|
||||||
|
msgid "Muted"
|
||||||
|
msgstr "発言禁止"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:123 templates/mumble/player.html:18
|
||||||
|
msgid "Deafened"
|
||||||
|
msgstr "聴取禁止"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:124 templates/mumble/player.html:21
|
||||||
|
msgid "Muted by self"
|
||||||
|
msgstr "自分で発言禁止"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:125 templates/mumble/player.html:24
|
||||||
|
msgid "Deafened by self"
|
||||||
|
msgstr "自分で聴取禁止"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:127
|
||||||
|
msgid "IP Address"
|
||||||
|
msgstr "IPアドレス"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:131
|
||||||
|
msgid "User"
|
||||||
|
msgstr "ユーザ"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:134
|
||||||
|
msgid "Full Name"
|
||||||
|
msgstr "名前"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:137
|
||||||
|
msgid "Sign-up date"
|
||||||
|
msgstr "登録日"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:142
|
||||||
|
msgid "User Comment"
|
||||||
|
msgstr "ユーザアカウント"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:154 templates/mumble/mumble.html.py:168
|
||||||
|
msgid "Kick user"
|
||||||
|
msgstr "ユーザをキック"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:160
|
||||||
|
msgid "Reason"
|
||||||
|
msgstr "意味"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:165
|
||||||
|
msgid "Ban user"
|
||||||
|
msgstr "ユーザを追放"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:175
|
||||||
|
msgid "Channel"
|
||||||
|
msgstr "チャンネル"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:177
|
||||||
|
msgid "Channel ID"
|
||||||
|
msgstr "チャンネルID"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:179
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "接続"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:182
|
||||||
|
msgid "Channel description"
|
||||||
|
msgstr "チャンネル説明"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:229
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "削除"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:265
|
||||||
|
msgid "Server Info"
|
||||||
|
msgstr "サーバ情報"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:266
|
||||||
|
msgid "Registration"
|
||||||
|
msgstr "登録"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:275
|
||||||
|
msgid "Administration"
|
||||||
|
msgstr "管理"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:282
|
||||||
|
msgid "User List"
|
||||||
|
msgstr "ユーザ一覧"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:286
|
||||||
|
msgid "name"
|
||||||
|
msgstr "名前"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:306
|
||||||
|
msgid "Change password"
|
||||||
|
msgstr "パスワード"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:319
|
||||||
|
msgid "Add"
|
||||||
|
msgstr "追加"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:331
|
||||||
|
msgid "Save"
|
||||||
|
msgstr "セーブ"
|
||||||
|
|
||||||
|
#: templates/mumble/mumble.html:357
|
||||||
|
msgid "Resync with Murmur"
|
||||||
|
msgstr "Murmurと再同期"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:15
|
||||||
|
msgid "Suppressed"
|
||||||
|
msgstr "鎮められた"
|
||||||
|
|
||||||
|
#: templates/mumble/player.html:27
|
||||||
|
msgid "has a User Comment set"
|
||||||
|
msgstr "コメントをセットされています"
|
||||||
|
|
||||||
|
#~ msgid "comment"
|
||||||
|
#~ msgstr "コメント"
|
||||||
|
|
||||||
|
#~ msgid "hash"
|
||||||
|
#~ msgstr "ハッシュ"
|
||||||
|
|
||||||
|
#~ msgid "Enter the ID of the default channel here. The Channel viewer displays the ID to server admins on the channel detail page."
|
||||||
|
#~ msgstr "接続開始時に利用されるデフォルトチャンネルIDを指定して下さい。チャンネル表示一覧にて チャンネル詳細を確認すればチャンネルIDを調べられます。."
|
||||||
|
|
||||||
|
#~ msgid "The admin group was not found in the ACL's groups list!"
|
||||||
|
#~ msgstr "管理者グループが権限一覧に見当たりません."
|
||||||
@@ -14,9 +14,43 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from server_detect import find_existing_instances
|
from shutil import copy, move
|
||||||
|
from os.path import exists, join
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import connection
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
|
|
||||||
from mumble import models
|
from mumble import models
|
||||||
|
|
||||||
|
from update_schema import update_schema
|
||||||
|
from server_detect import find_existing_instances
|
||||||
|
|
||||||
|
|
||||||
|
if settings.DATABASE_ENGINE == "sqlite3":
|
||||||
|
# Move the DB to the db subdirectory if necessary.
|
||||||
|
oldpath = join( settings.MUMBLE_DJANGO_ROOT, "mumble-django.db3" )
|
||||||
|
if not exists( settings.DATABASE_NAME ) and exists( oldpath ):
|
||||||
|
move( oldpath, settings.DATABASE_NAME )
|
||||||
|
|
||||||
|
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
tablename = models.Mumble._meta.db_table
|
||||||
|
|
||||||
|
if tablename in connection.introspection.get_table_list(cursor):
|
||||||
|
fields = connection.introspection.get_table_description(cursor, tablename)
|
||||||
|
uptodate = "server_id" in [ entry[0] for entry in fields ]
|
||||||
|
else:
|
||||||
|
# Table doesn't yet exist, so syncdb will create it properly
|
||||||
|
uptodate = True
|
||||||
|
|
||||||
|
if not uptodate:
|
||||||
|
if settings.DATABASE_ENGINE == "sqlite3":
|
||||||
|
# backup the db before the conversion.
|
||||||
|
copy( settings.DATABASE_NAME, settings.DATABASE_NAME+".bak" )
|
||||||
|
signals.post_syncdb.connect( update_schema, sender=models );
|
||||||
|
else:
|
||||||
signals.post_syncdb.connect( find_existing_instances, sender=models );
|
signals.post_syncdb.connect( find_existing_instances, sender=models );
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os, Ice
|
import os
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@@ -28,8 +28,16 @@ class TestFailed( Exception ):
|
|||||||
pass;
|
pass;
|
||||||
|
|
||||||
class Command( BaseCommand ):
|
class Command( BaseCommand ):
|
||||||
|
help = "Run a few tests on Mumble-Django's setup."
|
||||||
|
|
||||||
def handle(self, **options):
|
def handle(self, **options):
|
||||||
|
try:
|
||||||
|
import Ice
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
self.check_slice();
|
self.check_slice();
|
||||||
|
|
||||||
self.check_rootdir();
|
self.check_rootdir();
|
||||||
self.check_dbase();
|
self.check_dbase();
|
||||||
self.check_sites();
|
self.check_sites();
|
||||||
@@ -166,10 +174,10 @@ class Command( BaseCommand ):
|
|||||||
else:
|
else:
|
||||||
for mumble in mm:
|
for mumble in mm:
|
||||||
try:
|
try:
|
||||||
mumble.getCtl();
|
mumble.ctl
|
||||||
except Ice.Exception, err:
|
except Exception, err:
|
||||||
raise TestFailed(
|
raise TestFailed(
|
||||||
"Connecting to Murmur `%s` (%s) failed: %s" % ( mumble.name, mumble.dbus, err )
|
"Connecting to Murmur `%s` (%s) failed: %s" % ( mumble.name, mumble.server, err )
|
||||||
);
|
);
|
||||||
print "[ OK ]";
|
print "[ OK ]";
|
||||||
|
|
||||||
|
|||||||
56
mumble/management/commands/getslice.py
Normal file
56
mumble/management/commands/getslice.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Ice, IcePy, os, getpass
|
||||||
|
from sys import stderr
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from mumble.models import MumbleServer
|
||||||
|
|
||||||
|
class Command( BaseCommand ):
|
||||||
|
help = "Check if the known servers support getSlice."
|
||||||
|
|
||||||
|
def handle(self, **options):
|
||||||
|
prop = Ice.createProperties([])
|
||||||
|
prop.setProperty("Ice.ImplicitContext", "Shared")
|
||||||
|
|
||||||
|
idd = Ice.InitializationData()
|
||||||
|
idd.properties = prop
|
||||||
|
|
||||||
|
ice = Ice.initialize(idd)
|
||||||
|
|
||||||
|
for serv in MumbleServer.objects.all():
|
||||||
|
print "Probing server at '%s'..." % serv.dbus
|
||||||
|
|
||||||
|
if serv.secret:
|
||||||
|
ice.getImplicitContext().put( "secret", serv.secret.encode("utf-8") )
|
||||||
|
|
||||||
|
prx = ice.stringToProxy( serv.dbus.encode("utf-8") )
|
||||||
|
|
||||||
|
# Try loading the Slice from Murmur directly via its getSlice method.
|
||||||
|
try:
|
||||||
|
slice = IcePy.Operation( 'getSlice',
|
||||||
|
Ice.OperationMode.Idempotent, Ice.OperationMode.Idempotent,
|
||||||
|
True, (), (), (), IcePy._t_string, ()
|
||||||
|
).invoke(prx, ((), None))
|
||||||
|
except TypeError, err:
|
||||||
|
print " Received TypeError:", err
|
||||||
|
print " It seems your version of IcePy is incompatible."
|
||||||
|
except Ice.OperationNotExistException:
|
||||||
|
print " Your version of Murmur does not support getSlice."
|
||||||
|
else:
|
||||||
|
print " Successfully received the slice (length: %d bytes.)" % len(slice)
|
||||||
@@ -14,11 +14,12 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os, getpass
|
||||||
|
|
||||||
|
from django.db import DatabaseError
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from mumble import models
|
from mumble.models import MumbleServer, Mumble
|
||||||
from mumble.mctl import MumbleCtlBase
|
from mumble.mctl import MumbleCtlBase
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ def find_existing_instances( **kwargs ):
|
|||||||
print " 2) ICE -- Meta:tcp -h 127.0.0.1 -p 6502"
|
print " 2) ICE -- Meta:tcp -h 127.0.0.1 -p 6502"
|
||||||
print "Enter 1 or 2 for the defaults above, nothing to skip Server detection,"
|
print "Enter 1 or 2 for the defaults above, nothing to skip Server detection,"
|
||||||
print "and if the defaults do not fit your needs, enter the correct string."
|
print "and if the defaults do not fit your needs, enter the correct string."
|
||||||
print "Whether to use DBus or ICE will be detected automatically from the"
|
print "Whether to use DBus or Ice will be detected automatically from the"
|
||||||
print "string's format."
|
print "string's format."
|
||||||
print
|
print
|
||||||
|
|
||||||
@@ -78,8 +79,10 @@ def find_existing_instances( **kwargs ):
|
|||||||
elif dbusName == "2":
|
elif dbusName == "2":
|
||||||
dbusName = "Meta:tcp -h 127.0.0.1 -p 6502";
|
dbusName = "Meta:tcp -h 127.0.0.1 -p 6502";
|
||||||
|
|
||||||
|
icesecret = getpass.getpass("Please enter the Ice secret (if any): ");
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ctl = MumbleCtlBase.newInstance( dbusName, settings.SLICE );
|
ctl = MumbleCtlBase.newInstance( dbusName, settings.SLICE, icesecret );
|
||||||
except Exception, instance:
|
except Exception, instance:
|
||||||
if v:
|
if v:
|
||||||
print "Unable to connect using name %s. The error was:" % dbusName;
|
print "Unable to connect using name %s. The error was:" % dbusName;
|
||||||
@@ -92,40 +95,71 @@ def find_existing_instances( **kwargs ):
|
|||||||
|
|
||||||
servIDs = ctl.getAllServers();
|
servIDs = ctl.getAllServers();
|
||||||
|
|
||||||
|
try:
|
||||||
|
meta = MumbleServer.objects.get( dbus=dbusName );
|
||||||
|
except MumbleServer.DoesNotExist:
|
||||||
|
meta = MumbleServer( dbus=dbusName );
|
||||||
|
finally:
|
||||||
|
meta.secret = icesecret;
|
||||||
|
meta.save();
|
||||||
|
|
||||||
for id in servIDs:
|
for id in servIDs:
|
||||||
if v > 1:
|
if v > 1:
|
||||||
print "Checking Murmur instance with id %d." % id;
|
print "Checking Murmur instance with id %d." % id;
|
||||||
# first check that the server has not yet been inserted into the DB
|
# first check that the server has not yet been inserted into the DB
|
||||||
try:
|
try:
|
||||||
instance = models.Mumble.objects.get( dbus=dbusName, srvid=id );
|
instance = Mumble.objects.get( server=meta, srvid=id );
|
||||||
except models.Mumble.DoesNotExist:
|
except Mumble.DoesNotExist:
|
||||||
values = {
|
values = {
|
||||||
|
"server": meta,
|
||||||
"srvid": id,
|
"srvid": id,
|
||||||
"dbus": dbusName,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v > 1:
|
if v:
|
||||||
print "Found new Murmur instance %d on bus '%s'... " % ( id, dbusName ),
|
print "Found new Murmur instance %d on bus '%s'... " % ( id, dbusName )
|
||||||
|
|
||||||
# now create a model for the record set.
|
# now create a model for the record set.
|
||||||
instance = models.Mumble( **values );
|
instance = Mumble( **values );
|
||||||
else:
|
else:
|
||||||
if v > 1:
|
if v:
|
||||||
print "Syncing Murmur instance... ",
|
print "Syncing Murmur instance %d: '%s'... " % ( instance.id, instance.name )
|
||||||
|
|
||||||
|
try:
|
||||||
instance.configureFromMurmur();
|
instance.configureFromMurmur();
|
||||||
|
except DatabaseError, err:
|
||||||
if v > 1:
|
try:
|
||||||
print instance.name;
|
# Find instances with the same address/port
|
||||||
|
dup = Mumble.objects.get( addr=instance.addr, port=instance.port )
|
||||||
instance.save( dontConfigureMurmur=True );
|
except Mumble.DoesNotExist:
|
||||||
|
# None exist - this must've been something else.
|
||||||
|
print "Server ID / Name: %d / %s" % ( instance.srvid, instance.name )
|
||||||
|
raise err
|
||||||
|
else:
|
||||||
|
print "ERROR: There is already another server instance registered"
|
||||||
|
print " on the same address and port."
|
||||||
|
print " -------------"
|
||||||
|
print " New Server ID:", instance.srvid,
|
||||||
|
print " New Server Name:", instance.name
|
||||||
|
print " Address:", instance.addr
|
||||||
|
print " Port:", instance.port
|
||||||
|
print " Connection string:", instance.server.dbus
|
||||||
|
print " -------------"
|
||||||
|
print " Duplicate Server ID:", dup.srvid,
|
||||||
|
print "Duplicate Server Name:", dup.name
|
||||||
|
print " Address:", dup.addr
|
||||||
|
print " Port:", dup.port
|
||||||
|
print " Connection string:", dup.server.dbus
|
||||||
|
return False
|
||||||
|
except Exception, err:
|
||||||
|
print "Server ID / Name: %d / %s" % ( instance.srvid, instance.name )
|
||||||
|
raise err
|
||||||
|
|
||||||
# Now search for players on this server that have not yet been registered
|
# Now search for players on this server that have not yet been registered
|
||||||
if instance.booted:
|
if instance.booted:
|
||||||
if v > 1:
|
if v > 1:
|
||||||
print "Looking for registered Players on Server id %d." % id;
|
print "Looking for registered Players on Server id %d." % id;
|
||||||
instance.readUsersFromMurmur( verbose=v );
|
instance.readUsersFromMurmur( verbose=v );
|
||||||
elif v > 1:
|
elif v:
|
||||||
print "This server is not running, can't sync players.";
|
print "This server is not running, can't sync players.";
|
||||||
|
|
||||||
if v > 1:
|
if v > 1:
|
||||||
|
|||||||
76
mumble/management/update_schema.py
Normal file
76
mumble/management/update_schema.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
* Copyright © 2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from os.path import join
|
||||||
|
|
||||||
|
from django.db import connection, transaction
|
||||||
|
from django.db.models import signals
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from mumble import models
|
||||||
|
from mumble.management.server_detect import find_existing_instances
|
||||||
|
|
||||||
|
|
||||||
|
def update_schema( **kwargs ):
|
||||||
|
if "verbosity" in kwargs:
|
||||||
|
v = kwargs['verbosity'];
|
||||||
|
else:
|
||||||
|
v = 1;
|
||||||
|
|
||||||
|
if v:
|
||||||
|
print "Migrating Database schema for Mumble-Django 2.0 now."
|
||||||
|
|
||||||
|
scriptdir = join( settings.CONVERSIONSQL_ROOT, {
|
||||||
|
'postgresql_psycopg2': 'pgsql',
|
||||||
|
'postgresql': 'pgsql',
|
||||||
|
'mysql': 'mysql',
|
||||||
|
'sqlite3': 'sqlite',
|
||||||
|
}[settings.DATABASE_ENGINE] )
|
||||||
|
|
||||||
|
if v > 1:
|
||||||
|
print "Reading migration scripts for %s from '%s'" % ( settings.DATABASE_ENGINE, scriptdir )
|
||||||
|
|
||||||
|
scripts = [ filename for filename in os.listdir( scriptdir ) if filename.endswith( ".sql" ) ]
|
||||||
|
scripts.sort()
|
||||||
|
|
||||||
|
for filename in scripts:
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
scriptpath = os.path.join( scriptdir, filename )
|
||||||
|
scriptfile = open( scriptpath, "r" )
|
||||||
|
try:
|
||||||
|
if v > 1:
|
||||||
|
print "Running migration script '%s'..." % scriptpath
|
||||||
|
stmt = scriptfile.read()
|
||||||
|
cursor.execute( stmt )
|
||||||
|
|
||||||
|
except IOError, err:
|
||||||
|
print "Error reading file '%s':" % filename
|
||||||
|
print err
|
||||||
|
|
||||||
|
except cursor.db.connection.Error, err:
|
||||||
|
print "Error executing file '%s':" % filename
|
||||||
|
print err
|
||||||
|
|
||||||
|
finally:
|
||||||
|
scriptfile.close()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
if v:
|
||||||
|
print "Database migration finished successfully."
|
||||||
|
|
||||||
|
find_existing_instances( **kwargs )
|
||||||
@@ -22,84 +22,17 @@ class MumbleCtlBase (object):
|
|||||||
|
|
||||||
cache = {};
|
cache = {};
|
||||||
|
|
||||||
def getAllConf(self, srvid):
|
|
||||||
raise NotImplementedError( "mctl::getAllConf" );
|
|
||||||
|
|
||||||
def getVersion( self ):
|
|
||||||
raise NotImplementedError( "mctl::getVersion" );
|
|
||||||
|
|
||||||
def getConf(self, srvid, key):
|
|
||||||
raise NotImplementedError( "mctl::getConf" );
|
|
||||||
|
|
||||||
def setConf(self, srvid, key, value):
|
|
||||||
raise NotImplementedError( "mctl::setConf" );
|
|
||||||
|
|
||||||
def getDefaultConf(self):
|
|
||||||
raise NotImplementedError( "mctl::getDefaultConf" );
|
|
||||||
|
|
||||||
def newServer(self):
|
|
||||||
raise NotImplementedError( "mctl::newServer" );
|
|
||||||
|
|
||||||
def setSuperUserPassword(self, srvid, value):
|
|
||||||
raise NotImplementedError( "mctl::setSuperUserPassword" );
|
|
||||||
|
|
||||||
def start(self, srvid):
|
|
||||||
raise NotImplementedError( "mctl::start" );
|
|
||||||
|
|
||||||
def stop(self, srvid):
|
|
||||||
raise NotImplementedError( "mctl::stop" );
|
|
||||||
|
|
||||||
def isBooted(self, srvid):
|
|
||||||
raise NotImplementedError( "mctl::isBooted" );
|
|
||||||
|
|
||||||
def deleteServer(self, srvid):
|
|
||||||
raise NotImplementedError( "mctl::deleteServer" );
|
|
||||||
|
|
||||||
def getPlayers(self, srvid):
|
|
||||||
raise NotImplementedError( "mctl::getPlayers" );
|
|
||||||
|
|
||||||
def getRegisteredPlayers(self, srvid, filter):
|
|
||||||
raise NotImplementedError( "mctl::getRegisteredPlayers" );
|
|
||||||
|
|
||||||
def getChannels(self, srvid):
|
|
||||||
raise NotImplementedError( "mctl::getChannels" );
|
|
||||||
|
|
||||||
def registerPlayer(self, srvid, name, email, password):
|
|
||||||
raise NotImplementedError( "mctl::registerPlayer" );
|
|
||||||
|
|
||||||
def getRegistration(self, srvid, mumbleid):
|
|
||||||
raise NotImplementedError( "mctl::getRegistration" );
|
|
||||||
|
|
||||||
def setRegistration(self, srvid, mumbleid, name, email, password):
|
|
||||||
raise NotImplementedError( "mctl::setRegistration" );
|
|
||||||
|
|
||||||
def unregisterPlayer(self, srvid, mumbleid):
|
|
||||||
raise NotImplementedError( "mctl::unregisterPlayer" );
|
|
||||||
|
|
||||||
def getBootedServers(self):
|
|
||||||
raise NotImplementedError( "mctl::getBootedServers" );
|
|
||||||
|
|
||||||
def getAllServers(self):
|
|
||||||
raise NotImplementedError( "mctl::getAllServers" );
|
|
||||||
|
|
||||||
def getACL(self, srvid, channelid):
|
|
||||||
raise NotImplementedError( "mctl::getACL" );
|
|
||||||
|
|
||||||
def setACL(self, srvid, channelid, acl, groups, inherit):
|
|
||||||
raise NotImplementedError( "mctl::setACL" );
|
|
||||||
|
|
||||||
def getTexture(self, srvid, mumbleid):
|
|
||||||
raise NotImplementedError( "mctl::getTexture" );
|
|
||||||
|
|
||||||
def setTexture(self, srvid, mumbleid, infile):
|
|
||||||
raise NotImplementedError( "mctl::setTexture" );
|
|
||||||
|
|
||||||
def verifyPassword( self, srvid, username, password ):
|
|
||||||
raise NotImplementedError( "mctl::verifyPassword" );
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def newInstance( connstring, slicefile ):
|
def newInstance( connstring, slicefile=None, icesecret=None ):
|
||||||
""" Create a new CTL object for the given connstring. """
|
""" Create a new CTL object for the given connstring.
|
||||||
|
|
||||||
|
Optional parameters are the path to the slice file and the
|
||||||
|
Ice secret necessary to authenticate to Murmur.
|
||||||
|
|
||||||
|
The path can be omitted only if using DBus or running Murmur
|
||||||
|
1.2.3 or later, which exports a getSlice method to retrieve
|
||||||
|
the Slice from.
|
||||||
|
"""
|
||||||
|
|
||||||
# check cache
|
# check cache
|
||||||
if connstring in MumbleCtlBase.cache:
|
if connstring in MumbleCtlBase.cache:
|
||||||
@@ -115,7 +48,7 @@ class MumbleCtlBase (object):
|
|||||||
ctl = MumbleCtlDbus( connstring )
|
ctl = MumbleCtlDbus( connstring )
|
||||||
else:
|
else:
|
||||||
from MumbleCtlIce import MumbleCtlIce
|
from MumbleCtlIce import MumbleCtlIce
|
||||||
ctl = MumbleCtlIce( connstring, slicefile )
|
ctl = MumbleCtlIce( connstring, slicefile, icesecret )
|
||||||
|
|
||||||
MumbleCtlBase.cache[connstring] = ctl;
|
MumbleCtlBase.cache[connstring] = ctl;
|
||||||
return ctl;
|
return ctl;
|
||||||
@@ -123,6 +56,3 @@ class MumbleCtlBase (object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def clearCache():
|
def clearCache():
|
||||||
MumbleCtlBase.cache = {};
|
MumbleCtlBase.cache = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,23 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import socket
|
||||||
import datetime
|
import datetime
|
||||||
|
import re
|
||||||
from time import time
|
from time import time
|
||||||
from os.path import join
|
|
||||||
|
|
||||||
from django.utils.http import urlquote
|
from django.utils.http import urlquote
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def cmp_channels( left, rite ):
|
||||||
|
""" Compare two channels, first by position, and if that equals, by name. """
|
||||||
|
if hasattr( left, "position" ) and hasattr( rite, "position" ):
|
||||||
|
byorder = cmp( left.position, rite.position );
|
||||||
|
if byorder != 0:
|
||||||
|
return byorder;
|
||||||
|
return cmp_names( left, rite );
|
||||||
|
|
||||||
def cmp_names( left, rite ):
|
def cmp_names( left, rite ):
|
||||||
""" Compare two objects by their name property. """
|
""" Compare two objects by their name property. """
|
||||||
return cmp( left.name, rite.name );
|
return cmp( left.name, rite.name );
|
||||||
@@ -65,7 +75,7 @@ class mmChannel( object ):
|
|||||||
|
|
||||||
return self._acl;
|
return self._acl;
|
||||||
|
|
||||||
acl = property( getACL, doc=getACL.__doc__ );
|
acl = property( getACL );
|
||||||
|
|
||||||
|
|
||||||
is_server = False;
|
is_server = False;
|
||||||
@@ -95,7 +105,7 @@ class mmChannel( object ):
|
|||||||
|
|
||||||
def sort( self ):
|
def sort( self ):
|
||||||
""" Sort my subchannels and players, and then iterate over them and sort them recursively. """
|
""" Sort my subchannels and players, and then iterate over them and sort them recursively. """
|
||||||
self.subchans.sort( cmp_names );
|
self.subchans.sort( cmp_channels );
|
||||||
self.players.sort( cmp_names );
|
self.players.sort( cmp_names );
|
||||||
for subc in self.subchans:
|
for subc in self.subchans:
|
||||||
subc.sort();
|
subc.sort();
|
||||||
@@ -113,26 +123,23 @@ class mmChannel( object ):
|
|||||||
""" Create an URL to connect to this channel. The URL is of the form
|
""" Create an URL to connect to this channel. The URL is of the form
|
||||||
mumble://username@host:port/parentchans/self.name
|
mumble://username@host:port/parentchans/self.name
|
||||||
"""
|
"""
|
||||||
userstr = "";
|
from urlparse import urlunsplit
|
||||||
|
versionstr = "version=%d.%d.%d" % tuple(self.server.version[:3]);
|
||||||
|
|
||||||
|
if self.parent is not None:
|
||||||
|
chanlist = self.parent_channels() + [self.name];
|
||||||
|
chanlist = [ urlquote( chan ) for chan in chanlist ];
|
||||||
|
urlpath = "/".join( chanlist );
|
||||||
|
else:
|
||||||
|
urlpath = "";
|
||||||
|
|
||||||
if for_user is not None:
|
if for_user is not None:
|
||||||
userstr = "%s@" % for_user.name;
|
netloc = "%s@%s" % ( for_user.name, self.server.netloc );
|
||||||
|
return urlunsplit(( "mumble", netloc, urlpath, versionstr, "" ))
|
||||||
|
else:
|
||||||
|
return urlunsplit(( "mumble", self.server.netloc, urlpath, versionstr, "" ))
|
||||||
|
|
||||||
versionstr = "version=%d.%d.%d" % tuple(self.server.version[0:3]);
|
connecturl = property( getURL );
|
||||||
|
|
||||||
# create list of all my parents and myself
|
|
||||||
chanlist = self.parent_channels() + [self.name];
|
|
||||||
# urlencode channel names
|
|
||||||
chanlist = [ urlquote( chan ) for chan in chanlist ];
|
|
||||||
# create a path by joining the channel names
|
|
||||||
chanpath = join( *chanlist );
|
|
||||||
|
|
||||||
if self.server.port != settings.MUMBLE_DEFAULT_PORT:
|
|
||||||
return "mumble://%s%s:%d/%s?%s" % ( userstr, self.server.addr, self.server.port, chanpath, versionstr );
|
|
||||||
|
|
||||||
return "mumble://%s%s/%s?%s" % ( userstr, self.server.addr, chanpath, versionstr );
|
|
||||||
|
|
||||||
connecturl = property( getURL, doc="A convenience wrapper for getURL." );
|
|
||||||
|
|
||||||
def setDefault( self ):
|
def setDefault( self ):
|
||||||
""" Make this the server's default channel. """
|
""" Make this the server's default channel. """
|
||||||
@@ -150,6 +157,32 @@ class mmChannel( object ):
|
|||||||
chandata['subchans'] = [ sc.asDict() for sc in self.subchans ];
|
chandata['subchans'] = [ sc.asDict() for sc in self.subchans ];
|
||||||
return chandata;
|
return chandata;
|
||||||
|
|
||||||
|
def asMvXml( self, parentnode ):
|
||||||
|
""" Return an XML tree for this channel suitable for MumbleViewer-ng. """
|
||||||
|
from xml.etree.cElementTree import SubElement
|
||||||
|
me = SubElement( parentnode, "item" , id=self.id, rel='channel' )
|
||||||
|
content = SubElement( me, "content" )
|
||||||
|
name = SubElement( content , "name" )
|
||||||
|
name.text = self.name
|
||||||
|
|
||||||
|
for sc in self.subchans:
|
||||||
|
sc.asMvXml(me)
|
||||||
|
for pl in self.players:
|
||||||
|
pl.asMvXml(me)
|
||||||
|
|
||||||
|
def asMvJson( self ):
|
||||||
|
""" Return a Dict for this channel suitable for MumbleViewer-ng. """
|
||||||
|
return {
|
||||||
|
"attributes": {
|
||||||
|
"href": self.connecturl,
|
||||||
|
"id": self.id,
|
||||||
|
"rel": "channel",
|
||||||
|
},
|
||||||
|
"data": self.name,
|
||||||
|
"children": [ sc.asMvJson() for sc in self.subchans ] + \
|
||||||
|
[ pl.asMvJson() for pl in self.players ],
|
||||||
|
"state": { False: "closed", True: "open" }[self.top_or_not_empty],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -201,6 +234,21 @@ class mmPlayer( object ):
|
|||||||
is_channel = False;
|
is_channel = False;
|
||||||
is_player = True;
|
is_player = True;
|
||||||
|
|
||||||
|
def getIpAsString( self ):
|
||||||
|
""" Get the client's IPv4 or IPv6 address, in a pretty format. """
|
||||||
|
addr = self.player_obj.address;
|
||||||
|
if max( addr[:10] ) == 0 and addr[10:12] == (255, 255):
|
||||||
|
return "%d.%d.%d.%d" % tuple( addr[12:] );
|
||||||
|
ip6addr = [(hi << 8 | lo) for (hi, lo) in zip(addr[0::2], addr[1::2])]
|
||||||
|
# colon-separated string:
|
||||||
|
ipstr = ':'.join([ ("%x" % part) for part in ip6addr ]);
|
||||||
|
# 0:0:0 -> ::
|
||||||
|
return re.sub( "((^|:)(0:){2,})", '::', ipstr, 1 );
|
||||||
|
|
||||||
|
ipaddress = property( getIpAsString );
|
||||||
|
fqdn = property( lambda self: socket.getfqdn( self.ipaddress ),
|
||||||
|
doc="The fully qualified domain name of the user's host." );
|
||||||
|
|
||||||
# kept for compatibility to mmChannel (useful for traversal funcs)
|
# kept for compatibility to mmChannel (useful for traversal funcs)
|
||||||
playerCount = property( lambda self: -1, doc="Exists only for compatibility to mmChannel." );
|
playerCount = property( lambda self: -1, doc="Exists only for compatibility to mmChannel." );
|
||||||
|
|
||||||
@@ -222,6 +270,24 @@ class mmPlayer( object ):
|
|||||||
|
|
||||||
return pldata;
|
return pldata;
|
||||||
|
|
||||||
|
def asMvXml( self, parentnode ):
|
||||||
|
""" Return an XML node for this player suitable for MumbleViewer-ng. """
|
||||||
|
from xml.etree.cElementTree import SubElement
|
||||||
|
me = SubElement( parentnode, "item" , id=self.id, rel='user' )
|
||||||
|
content = SubElement( me, "content" )
|
||||||
|
name = SubElement( content , "name" )
|
||||||
|
name.text = self.name
|
||||||
|
|
||||||
|
def asMvJson( self ):
|
||||||
|
""" Return a Dict for this player suitable for MumbleViewer-ng. """
|
||||||
|
return {
|
||||||
|
"attributes": {
|
||||||
|
"id": self.id,
|
||||||
|
"rel": "user",
|
||||||
|
},
|
||||||
|
'data': self.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class mmACL( object ):
|
class mmACL( object ):
|
||||||
|
|||||||
435
mumble/models.py
435
mumble/models.py
@@ -14,9 +14,10 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import socket
|
import socket, Ice, re
|
||||||
|
from sys import stderr
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_noop, ugettext_lazy as _
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
@@ -26,20 +27,119 @@ from mumble.mmobjects import mmChannel, mmPlayer
|
|||||||
from mumble.mctl import MumbleCtlBase
|
from mumble.mctl import MumbleCtlBase
|
||||||
|
|
||||||
|
|
||||||
def mk_config_property( field, doc="" ):
|
def mk_config_property( field, doc="", get_coerce=None, get_none=None, set_coerce=unicode, set_none='' ):
|
||||||
""" Create a property for the given config field. """
|
""" Create a property for the given config field. """
|
||||||
|
|
||||||
def get_field( self ):
|
def get_field( self ):
|
||||||
if self.id is not None:
|
if self.id is not None:
|
||||||
return self.getConf( field )
|
val = self.getConf( field );
|
||||||
else:
|
if val is None or val == '':
|
||||||
|
return get_none
|
||||||
|
if callable(get_coerce):
|
||||||
|
return get_coerce( val )
|
||||||
|
return val
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def set_field( self, value ):
|
def set_field( self, value ):
|
||||||
|
if value is None:
|
||||||
|
self.setConf( field, set_none )
|
||||||
|
elif callable(set_coerce):
|
||||||
|
self.setConf( field, set_coerce(value) )
|
||||||
|
else:
|
||||||
self.setConf( field, value )
|
self.setConf( field, value )
|
||||||
|
|
||||||
return property( get_field, set_field, doc=doc )
|
return property( get_field, set_field, doc=doc )
|
||||||
|
|
||||||
|
def mk_config_bool_property( field, doc="" ):
|
||||||
|
return mk_config_property( field, doc=doc,
|
||||||
|
get_coerce = lambda value: value == "true",
|
||||||
|
set_coerce = lambda value: str(value).lower()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
class MumbleServer( models.Model ):
|
||||||
|
""" Represents a Murmur server installation. """
|
||||||
|
|
||||||
|
dbus = models.CharField( _('DBus or ICE base'), max_length=200, unique=True, default=settings.DEFAULT_CONN, help_text=_(
|
||||||
|
"Examples: 'net.sourceforge.mumble.murmur' for DBus or 'Meta:tcp -h 127.0.0.1 -p 6502' for Ice.") );
|
||||||
|
secret = models.CharField( _('Ice Secret'), max_length=200, blank=True );
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Mumble Server');
|
||||||
|
verbose_name_plural = _('Mumble Servers');
|
||||||
|
|
||||||
|
def __init__( self, *args, **kwargs ):
|
||||||
|
models.Model.__init__( self, *args, **kwargs );
|
||||||
|
self._ctl = None;
|
||||||
|
self._conf = None;
|
||||||
|
|
||||||
|
def __unicode__( self ):
|
||||||
|
return self.dbus;
|
||||||
|
|
||||||
|
# Ctl instantiation
|
||||||
|
def getCtl( self ):
|
||||||
|
""" Instantiate and return a MumbleCtl object for this server.
|
||||||
|
|
||||||
|
Only one instance will be created, and reused on subsequent calls.
|
||||||
|
"""
|
||||||
|
if not self._ctl:
|
||||||
|
self._ctl = MumbleCtlBase.newInstance( self.dbus, settings.SLICE, self.secret );
|
||||||
|
return self._ctl;
|
||||||
|
|
||||||
|
ctl = property( getCtl, doc="Get a Control object for this server. The ctl is cached for later reuse." );
|
||||||
|
|
||||||
|
def isMethodDbus(self):
|
||||||
|
""" Return true if this instance uses DBus. """
|
||||||
|
rd = re.compile( r'^(\w+\.)*\w+$' );
|
||||||
|
return bool(rd.match(self.dbus))
|
||||||
|
|
||||||
|
method_dbus = property( isMethodDbus )
|
||||||
|
method_ice = property( lambda self: not self.isMethodDbus(), doc="Return true if this instance uses Ice." )
|
||||||
|
|
||||||
|
def getDefaultConf( self, field=None ):
|
||||||
|
""" Get a field from the default conf dictionary, or None if the field isn't set. """
|
||||||
|
if self._conf is None:
|
||||||
|
self._conf = self.ctl.getDefaultConf()
|
||||||
|
if field is None:
|
||||||
|
return self._conf
|
||||||
|
if field in self._conf:
|
||||||
|
return self._conf[field]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def isOnline( self ):
|
||||||
|
""" Return true if this server process is running. """
|
||||||
|
possibleexceptions = []
|
||||||
|
try:
|
||||||
|
from Ice import ConnectionRefusedException
|
||||||
|
except ImportError, err:
|
||||||
|
if self.method_ice:
|
||||||
|
print >> stderr, err
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
possibleexceptions.append( ConnectionRefusedException )
|
||||||
|
try:
|
||||||
|
from dbus import DBusException
|
||||||
|
except ImportError, err:
|
||||||
|
if self.method_dbus:
|
||||||
|
print >> stderr, err
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
possibleexceptions.append( DBusException )
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.ctl
|
||||||
|
except tuple(possibleexceptions), err:
|
||||||
|
print >> stderr, err
|
||||||
|
return False
|
||||||
|
except (EnvironmentError, RuntimeError), err:
|
||||||
|
print >> stderr, err
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
online = property( isOnline )
|
||||||
|
defaultconf = property( getDefaultConf, doc="The default config dictionary." )
|
||||||
|
|
||||||
|
|
||||||
class Mumble( models.Model ):
|
class Mumble( models.Model ):
|
||||||
""" Represents a Murmur server instance.
|
""" Represents a Murmur server instance.
|
||||||
@@ -56,42 +156,49 @@ class Mumble( models.Model ):
|
|||||||
deleted as well.
|
deleted as well.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
server = models.ForeignKey( MumbleServer, verbose_name=_("Mumble Server") );
|
||||||
name = models.CharField( _('Server Name'), max_length=200 );
|
name = models.CharField( _('Server Name'), max_length=200 );
|
||||||
dbus = models.CharField( _('DBus or ICE base'), max_length = 200, default = settings.DEFAULT_CONN, help_text=_(
|
|
||||||
"Examples: 'net.sourceforge.mumble.murmur' for DBus or 'Meta:tcp -h 127.0.0.1 -p 6502' for Ice.") );
|
|
||||||
srvid = models.IntegerField( _('Server ID'), editable=False );
|
srvid = models.IntegerField( _('Server ID'), editable=False );
|
||||||
addr = models.CharField( _('Server Address'), max_length = 200, help_text=_(
|
addr = models.CharField( _('Server Address'), max_length=200, blank=True, help_text=_(
|
||||||
"Hostname or IP address to bind to. You should use a hostname here, because it will appear on the "
|
"Hostname or IP address to bind to. You should use a hostname here, because it will appear on the "
|
||||||
"global server list.") );
|
"global server list.") );
|
||||||
port = models.IntegerField( _('Server Port'), default=settings.MUMBLE_DEFAULT_PORT, help_text=_(
|
port = models.IntegerField( _('Server Port'), blank=True, null=True, help_text=_(
|
||||||
"Port number to bind to. Use -1 to auto assign one.") );
|
"Port number to bind to. Leave empty to auto assign one.") );
|
||||||
|
display = models.CharField( _('Server Display Address'), max_length=200, blank=True, help_text=_(
|
||||||
|
"This field is only relevant if you are located behind a NAT, and names the Hostname or IP address "
|
||||||
|
"to use in the Channel Viewer and for the global server list registration. If not given, the addr "
|
||||||
|
"and port fields are used. If display and bind ports are equal, you can omit it here.") );
|
||||||
|
|
||||||
supw = property( lambda self: '',
|
supw = property( lambda self: '',
|
||||||
lambda self, value: self.ctl.setSuperUserPassword( self.srvid, value ),
|
lambda self, value: ( value and self.ctl.setSuperUserPassword( self.srvid, value ) ) or None,
|
||||||
doc='Superuser Password'
|
doc=_('Superuser Password')
|
||||||
)
|
)
|
||||||
|
|
||||||
url = mk_config_property( "registerurl", "Website URL" )
|
url = mk_config_property( "registerurl", ugettext_noop("Website URL") )
|
||||||
motd = mk_config_property( "welcometext", "Welcome Message" )
|
motd = mk_config_property( "welcometext", ugettext_noop("Welcome Message") )
|
||||||
passwd = mk_config_property( "password", "Server Password" )
|
passwd = mk_config_property( "password", ugettext_noop("Server Password") )
|
||||||
users = mk_config_property( "users", "Max. Users" )
|
users = mk_config_property( "users", ugettext_noop("Max. Users"), get_coerce=int )
|
||||||
bwidth = mk_config_property( "bandwidth", "Bandwidth [Bps]" )
|
bwidth = mk_config_property( "bandwidth", ugettext_noop("Bandwidth [Bps]"), get_coerce=int )
|
||||||
sslcrt = mk_config_property( "certificate", "SSL Certificate" )
|
sslcrt = mk_config_property( "certificate", ugettext_noop("SSL Certificate") )
|
||||||
sslkey = mk_config_property( "key", "SSL Key" )
|
sslkey = mk_config_property( "key", ugettext_noop("SSL Key") )
|
||||||
player = mk_config_property( "username", "Player name regex" )
|
player = mk_config_property( "username", ugettext_noop("Player name regex") )
|
||||||
channel = mk_config_property( "channelname", "Channel name regex" )
|
channel = mk_config_property( "channelname", ugettext_noop("Channel name regex") )
|
||||||
defchan = mk_config_property( "defaultchannel", "Default channel" )
|
defchan = mk_config_property( "defaultchannel", ugettext_noop("Default channel"), get_coerce=int )
|
||||||
|
timeout = mk_config_property( "timeout", ugettext_noop("Timeout"), get_coerce=int )
|
||||||
|
|
||||||
obfsc = property(
|
obfsc = mk_config_bool_property( "obfuscate", ugettext_noop("IP Obfuscation") )
|
||||||
lambda self: ( self.getConf( "obfuscate" ) == "true" ) if self.id is not None else None,
|
certreq = mk_config_bool_property( "certrequired", ugettext_noop("Require Certificate") )
|
||||||
lambda self, value: self.setConf( "obfuscate", str(value).lower() ),
|
textlen = mk_config_bool_property( "textmessagelength", ugettext_noop("Maximum length of text messages") )
|
||||||
doc="IP Obfuscation"
|
html = mk_config_bool_property( "allowhtml", ugettext_noop("Allow HTML to be used in messages") )
|
||||||
)
|
bonjour = mk_config_bool_property( "bonjour", ugettext_noop("Publish this server via Bonjour") )
|
||||||
|
autoboot= mk_config_bool_property( "boot", ugettext_noop("Boot Server when Murmur starts") )
|
||||||
|
|
||||||
def getBooted( self ):
|
def getBooted( self ):
|
||||||
if self.id is not None:
|
if self.id is not None:
|
||||||
return self.ctl.isBooted( self.srvid );
|
if self.server.online:
|
||||||
|
return self.ctl.isBooted( self.srvid )
|
||||||
|
else:
|
||||||
|
return None
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -102,10 +209,11 @@ class Mumble( models.Model ):
|
|||||||
else:
|
else:
|
||||||
self.ctl.stop( self.srvid );
|
self.ctl.stop( self.srvid );
|
||||||
|
|
||||||
booted = property( getBooted, setBooted, doc="Boot Server" )
|
booted = property( getBooted, setBooted, doc=ugettext_noop("Boot Server") )
|
||||||
|
online = property( getBooted, setBooted, doc=ugettext_noop("Boot Server") )
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ( ( 'dbus', 'srvid' ), ( 'addr', 'port' ), );
|
unique_together = ( ( 'server', 'srvid' ), );
|
||||||
verbose_name = _('Server instance');
|
verbose_name = _('Server instance');
|
||||||
verbose_name_plural = _('Server instances');
|
verbose_name_plural = _('Server instances');
|
||||||
|
|
||||||
@@ -119,52 +227,33 @@ class Mumble( models.Model ):
|
|||||||
but to Murmur as well.
|
but to Murmur as well.
|
||||||
"""
|
"""
|
||||||
if dontConfigureMurmur:
|
if dontConfigureMurmur:
|
||||||
# skip murmur configuration, e.g. because we're inserting models for existing servers.
|
|
||||||
return models.Model.save( self );
|
return models.Model.save( self );
|
||||||
|
|
||||||
# check if this server already exists, if not call newServer and set my srvid first
|
|
||||||
|
|
||||||
if self.id is None:
|
if self.id is None:
|
||||||
self.srvid = self.ctl.newServer();
|
self.srvid = self.ctl.newServer();
|
||||||
|
|
||||||
if self.port == -1:
|
|
||||||
self.port = max( [ rec['port'] for rec in Mumble.objects.values('port') ] ) + 1;
|
|
||||||
|
|
||||||
if self.port < 1 or self.port >= 2**16:
|
|
||||||
raise ValueError( _("Port number %(portno)d is not within the allowed range %(minrange)d - %(maxrange)d") % {
|
|
||||||
'portno': self.port,
|
|
||||||
'minrange': 1,
|
|
||||||
'maxrange': 2**16,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.ctl.setConf( self.srvid, 'host', socket.gethostbyname( self.addr ) );
|
|
||||||
self.ctl.setConf( self.srvid, 'port', str(self.port) );
|
|
||||||
self.ctl.setConf( self.srvid, 'registername', self.name );
|
self.ctl.setConf( self.srvid, 'registername', self.name );
|
||||||
self.ctl.setConf( self.srvid, 'registerurl', self.url );
|
|
||||||
|
|
||||||
# registerHostname needs to take the port no into account
|
if self.addr and self.addr != '0.0.0.0':
|
||||||
if self.port and self.port != settings.MUMBLE_DEFAULT_PORT:
|
self.ctl.setConf( self.srvid, 'host', socket.gethostbyname(self.addr) );
|
||||||
self.ctl.setConf( self.srvid, 'registerhostname', "%s:%d" % ( self.addr, self.port ) );
|
|
||||||
else:
|
else:
|
||||||
self.ctl.setConf( self.srvid, 'registerhostname', self.addr );
|
self.ctl.setConf( self.srvid, 'host', '' );
|
||||||
|
|
||||||
if self.supw:
|
if self.port and self.port != settings.MUMBLE_DEFAULT_PORT + self.srvid - 1:
|
||||||
self.ctl.setSuperUserPassword( self.srvid, self.supw );
|
self.ctl.setConf( self.srvid, 'port', str(self.port) );
|
||||||
self.supw = '';
|
|
||||||
|
|
||||||
if self.booted != self.ctl.isBooted( self.srvid ):
|
|
||||||
if self.booted:
|
|
||||||
self.ctl.start( self.srvid );
|
|
||||||
else:
|
else:
|
||||||
self.ctl.stop( self.srvid );
|
self.ctl.setConf( self.srvid, 'port', '' );
|
||||||
|
|
||||||
|
if self.netloc:
|
||||||
|
self.ctl.setConf( self.srvid, 'registerhostname', self.netloc );
|
||||||
|
else:
|
||||||
|
self.ctl.setConf( self.srvid, 'registerhostname', '' );
|
||||||
|
|
||||||
# Now allow django to save the record set
|
|
||||||
return models.Model.save( self );
|
return models.Model.save( self );
|
||||||
|
|
||||||
|
|
||||||
def __init__( self, *args, **kwargs ):
|
def __init__( self, *args, **kwargs ):
|
||||||
models.Model.__init__( self, *args, **kwargs );
|
models.Model.__init__( self, *args, **kwargs );
|
||||||
self._ctl = None;
|
|
||||||
self._channels = None;
|
self._channels = None;
|
||||||
self._rootchan = None;
|
self._rootchan = None;
|
||||||
|
|
||||||
@@ -172,26 +261,14 @@ class Mumble( models.Model ):
|
|||||||
users_regged = property( lambda self: self.mumbleuser_set.count(), doc="Number of registered users." );
|
users_regged = property( lambda self: self.mumbleuser_set.count(), doc="Number of registered users." );
|
||||||
users_online = property( lambda self: len(self.ctl.getPlayers(self.srvid)), doc="Number of online users." );
|
users_online = property( lambda self: len(self.ctl.getPlayers(self.srvid)), doc="Number of online users." );
|
||||||
channel_cnt = property( lambda self: len(self.ctl.getChannels(self.srvid)), doc="Number of channels." );
|
channel_cnt = property( lambda self: len(self.ctl.getChannels(self.srvid)), doc="Number of channels." );
|
||||||
is_public = property( lambda self: self.passwd == '',
|
is_public = property( lambda self: not self.passwd,
|
||||||
doc="False if a password is needed to join this server." );
|
doc="False if a password is needed to join this server." );
|
||||||
|
|
||||||
is_server = True;
|
is_server = True;
|
||||||
is_channel = False;
|
is_channel = False;
|
||||||
is_player = False;
|
is_player = False;
|
||||||
|
|
||||||
|
ctl = property( lambda self: self.server.ctl );
|
||||||
# Ctl instantiation
|
|
||||||
def getCtl( self ):
|
|
||||||
""" Instantiate and return a MumbleCtl object for this server.
|
|
||||||
|
|
||||||
Only one instance will be created, and reused on subsequent calls.
|
|
||||||
"""
|
|
||||||
if not self._ctl:
|
|
||||||
self._ctl = MumbleCtlBase.newInstance( self.dbus, settings.SLICE );
|
|
||||||
return self._ctl;
|
|
||||||
|
|
||||||
ctl = property( getCtl, doc="Get a Control object for this server. The ctl is cached for later reuse." );
|
|
||||||
|
|
||||||
|
|
||||||
def getConf( self, field ):
|
def getConf( self, field ):
|
||||||
return self.ctl.getConf( self.srvid, field )
|
return self.ctl.getConf( self.srvid, field )
|
||||||
@@ -200,38 +277,54 @@ class Mumble( models.Model ):
|
|||||||
return self.ctl.setConf( self.srvid, field, value )
|
return self.ctl.setConf( self.srvid, field, value )
|
||||||
|
|
||||||
def configureFromMurmur( self ):
|
def configureFromMurmur( self ):
|
||||||
default = self.ctl.getDefaultConf();
|
|
||||||
conf = self.ctl.getAllConf( self.srvid );
|
conf = self.ctl.getAllConf( self.srvid );
|
||||||
|
|
||||||
def find_in_dicts( keys, valueIfNotFound=None ):
|
if "registername" not in conf or not conf["registername"]:
|
||||||
if not isinstance( keys, tuple ):
|
self.name = "noname";
|
||||||
keys = ( keys, );
|
else:
|
||||||
|
self.name = conf["registername"];
|
||||||
|
|
||||||
for keyword in keys:
|
if "registerhostname" in conf and conf["registerhostname"]:
|
||||||
if keyword in conf:
|
if ':' in conf["registerhostname"]:
|
||||||
return conf[keyword];
|
regname, regport = conf["registerhostname"].split(':')
|
||||||
|
regport = int(regport)
|
||||||
|
else:
|
||||||
|
regname = conf["registerhostname"]
|
||||||
|
regport = None
|
||||||
|
else:
|
||||||
|
regname = None
|
||||||
|
regport = None
|
||||||
|
|
||||||
for keyword in keys:
|
if "host" in conf and conf["host"]:
|
||||||
keyword = keyword.lower();
|
addr = conf["host"]
|
||||||
if keyword in default:
|
else:
|
||||||
return default[keyword];
|
addr = None
|
||||||
|
|
||||||
return valueIfNotFound;
|
if "port" in conf and conf["port"]:
|
||||||
|
self.port = int(conf["port"])
|
||||||
|
else:
|
||||||
|
self.port = None
|
||||||
|
|
||||||
servername = find_in_dicts( "registername", "noname" );
|
if regname and addr:
|
||||||
if not servername:
|
if regport == self.port:
|
||||||
# RegistrationName was found in the dicts, but is an empty string
|
if socket.gethostbyname(regname) == socket.gethostbyname(addr):
|
||||||
servername = "noname";
|
self.display = ''
|
||||||
|
self.addr = regname
|
||||||
addr = find_in_dicts( ( "registerhostname", "host" ), "0.0.0.0" );
|
else:
|
||||||
if addr.find( ':' ) != -1:
|
self.display = regname
|
||||||
# The addr is a hostname which actually contains a port number, but we already got that from
|
self.addr = addr
|
||||||
# the port field, so we can simply drop it.
|
else:
|
||||||
addr = addr.split(':')[0];
|
self.display = conf["registerhostname"]
|
||||||
|
self.addr = addr
|
||||||
self.name = servername;
|
elif regname and not addr:
|
||||||
self.addr = addr;
|
self.display = regname
|
||||||
self.port = find_in_dicts( "port" );
|
self.addr = ''
|
||||||
|
elif addr and not regname:
|
||||||
|
self.display = ''
|
||||||
|
self.addr = addr
|
||||||
|
else:
|
||||||
|
self.display = ''
|
||||||
|
self.addr = ''
|
||||||
|
|
||||||
self.save( dontConfigureMurmur=True );
|
self.save( dontConfigureMurmur=True );
|
||||||
|
|
||||||
@@ -241,6 +334,9 @@ class Mumble( models.Model ):
|
|||||||
raise SystemError( "This murmur instance is not currently running, can't sync." );
|
raise SystemError( "This murmur instance is not currently running, can't sync." );
|
||||||
|
|
||||||
players = self.ctl.getRegisteredPlayers(self.srvid);
|
players = self.ctl.getRegisteredPlayers(self.srvid);
|
||||||
|
known_ids = [rec["mumbleid"]
|
||||||
|
for rec in MumbleUser.objects.filter( server=self ).values( "mumbleid" )
|
||||||
|
]
|
||||||
|
|
||||||
for idx in players:
|
for idx in players:
|
||||||
playerdata = players[idx];
|
playerdata = players[idx];
|
||||||
@@ -248,12 +344,9 @@ class Mumble( models.Model ):
|
|||||||
if playerdata.userid == 0: # Skip SuperUsers
|
if playerdata.userid == 0: # Skip SuperUsers
|
||||||
continue;
|
continue;
|
||||||
if verbose > 1:
|
if verbose > 1:
|
||||||
print "Checking Player with id %d and name '%s'." % ( playerdata.userid, playerdata.name );
|
print "Checking Player with id %d." % playerdata.userid;
|
||||||
|
|
||||||
try:
|
if playerdata.userid not in known_ids:
|
||||||
playerinstance = MumbleUser.objects.get( server=self, mumbleid=playerdata.userid );
|
|
||||||
|
|
||||||
except MumbleUser.DoesNotExist:
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print 'Found new Player "%s".' % playerdata.name;
|
print 'Found new Player "%s".' % playerdata.name;
|
||||||
|
|
||||||
@@ -267,8 +360,8 @@ class Mumble( models.Model ):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
if verbose > 1:
|
if verbose > 1:
|
||||||
print "This player is already listed in the database.";
|
print "Player '%s' is already known." % playerdata.name;
|
||||||
|
playerinstance = MumbleUser.objects.get( server=self, mumbleid=playerdata.userid );
|
||||||
playerinstance.name = playerdata.name;
|
playerinstance.name = playerdata.name;
|
||||||
|
|
||||||
playerinstance.save( dontConfigureMurmur=True );
|
playerinstance.save( dontConfigureMurmur=True );
|
||||||
@@ -277,6 +370,8 @@ class Mumble( models.Model ):
|
|||||||
def isUserAdmin( self, user ):
|
def isUserAdmin( self, user ):
|
||||||
""" Determine if the given user is an admin on this server. """
|
""" Determine if the given user is an admin on this server. """
|
||||||
if user.is_authenticated():
|
if user.is_authenticated():
|
||||||
|
if user.is_superuser:
|
||||||
|
return True;
|
||||||
try:
|
try:
|
||||||
return self.mumbleuser_set.get( owner=user ).getAdmin();
|
return self.mumbleuser_set.get( owner=user ).getAdmin();
|
||||||
except MumbleUser.DoesNotExist:
|
except MumbleUser.DoesNotExist:
|
||||||
@@ -355,20 +450,39 @@ class Mumble( models.Model ):
|
|||||||
channels = property( getChannels, doc="A convenience wrapper for getChannels()." );
|
channels = property( getChannels, doc="A convenience wrapper for getChannels()." );
|
||||||
rootchan = property( lambda self: self.channels[0], doc="A convenience wrapper for getChannels()[0]." );
|
rootchan = property( lambda self: self.channels[0], doc="A convenience wrapper for getChannels()[0]." );
|
||||||
|
|
||||||
|
def getNetloc( self ):
|
||||||
|
""" Return the address from the Display field (if any), or the server address.
|
||||||
|
Users from outside a NAT will need to use the Display address to connect
|
||||||
|
to this server instance.
|
||||||
|
"""
|
||||||
|
if self.display:
|
||||||
|
if ":" in self.display:
|
||||||
|
return self.display;
|
||||||
|
else:
|
||||||
|
daddr = self.display;
|
||||||
|
else:
|
||||||
|
daddr = self.addr;
|
||||||
|
|
||||||
|
if self.port and self.port != settings.MUMBLE_DEFAULT_PORT:
|
||||||
|
return "%s:%d" % (daddr, self.port);
|
||||||
|
else:
|
||||||
|
return daddr;
|
||||||
|
|
||||||
|
netloc = property( getNetloc );
|
||||||
|
|
||||||
def getURL( self, forUser = None ):
|
def getURL( self, forUser = None ):
|
||||||
""" Create an URL of the form mumble://username@host:port/ for this server. """
|
""" Create an URL of the form mumble://username@host:port/ for this server. """
|
||||||
userstr = "";
|
if not self.netloc:
|
||||||
|
return None
|
||||||
|
from urlparse import urlunsplit
|
||||||
|
versionstr = "version=%d.%d.%d" % tuple(self.version[:3]);
|
||||||
if forUser is not None:
|
if forUser is not None:
|
||||||
userstr = "%s@" % forUser.name;
|
netloc = "%s@%s" % ( forUser.name, self.netloc );
|
||||||
|
return urlunsplit(( "mumble", netloc, "", versionstr, "" ))
|
||||||
|
else:
|
||||||
|
return urlunsplit(( "mumble", self.netloc, "", versionstr, "" ))
|
||||||
|
|
||||||
versionstr = "version=%d.%d.%d" % tuple(self.version[0:3]);
|
connecturl = property( getURL );
|
||||||
|
|
||||||
if self.port != settings.MUMBLE_DEFAULT_PORT:
|
|
||||||
return "mumble://%s%s:%d/?%s" % ( userstr, self.addr, self.port, versionstr );
|
|
||||||
|
|
||||||
return "mumble://%s%s/?%s" % ( userstr, self.addr, versionstr );
|
|
||||||
|
|
||||||
connecturl = property( getURL, doc="A convenience wrapper for getURL()." );
|
|
||||||
|
|
||||||
version = property( lambda self: self.ctl.getVersion(), doc="The version of Murmur." );
|
version = property( lambda self: self.ctl.getVersion(), doc="The version of Murmur." );
|
||||||
|
|
||||||
@@ -378,6 +492,43 @@ class Mumble( models.Model ):
|
|||||||
'root': self.rootchan.asDict()
|
'root': self.rootchan.asDict()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
def asMvXml( self ):
|
||||||
|
""" Return an XML tree for this server suitable for MumbleViewer-ng. """
|
||||||
|
from xml.etree.cElementTree import Element
|
||||||
|
root = Element("root")
|
||||||
|
self.rootchan.asMvXml(root)
|
||||||
|
return root
|
||||||
|
|
||||||
|
def asMvJson( self ):
|
||||||
|
""" Return a Dict for this server suitable for MumbleViewer-ng. """
|
||||||
|
return self.rootchan.asMvJson()
|
||||||
|
|
||||||
|
# "server" field protection
|
||||||
|
def __setattr__( self, name, value ):
|
||||||
|
if name == 'server':
|
||||||
|
if self.id is not None and self.server != value:
|
||||||
|
raise AttributeError( _( "This field must not be updated once the record has been saved." ) );
|
||||||
|
|
||||||
|
models.Model.__setattr__( self, name, value );
|
||||||
|
|
||||||
|
def kickUser( self, sessionid, reason="" ):
|
||||||
|
return self.ctl.kickUser( self.srvid, sessionid, reason );
|
||||||
|
|
||||||
|
def banUser( self, sessionid, reason="" ):
|
||||||
|
return self.ctl.addBanForSession( self.srvid, sessionid, reason=reason );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def mk_registration_property( field, doc="" ):
|
||||||
|
""" Create a property for the given registration field. """
|
||||||
|
|
||||||
|
def get_field( self ):
|
||||||
|
if "comment" in self.registration:
|
||||||
|
return self.registration["comment"];
|
||||||
|
else:
|
||||||
|
return None;
|
||||||
|
|
||||||
|
return property( get_field, doc=doc )
|
||||||
|
|
||||||
|
|
||||||
class MumbleUser( models.Model ):
|
class MumbleUser( models.Model ):
|
||||||
@@ -399,6 +550,9 @@ class MumbleUser( models.Model ):
|
|||||||
server = models.ForeignKey( Mumble, verbose_name=_('Server instance'), related_name="mumbleuser_set" );
|
server = models.ForeignKey( Mumble, verbose_name=_('Server instance'), related_name="mumbleuser_set" );
|
||||||
owner = models.ForeignKey( User, verbose_name=_('Account owner'), related_name="mumbleuser_set", null=True, blank=True );
|
owner = models.ForeignKey( User, verbose_name=_('Account owner'), related_name="mumbleuser_set", null=True, blank=True );
|
||||||
|
|
||||||
|
comment = mk_registration_property( "comment", doc=ugettext_noop("The user's comment.") );
|
||||||
|
hash = mk_registration_property( "hash", doc=ugettext_noop("The user's hash.") );
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ( ( 'server', 'owner' ), ( 'server', 'mumbleid' ) );
|
unique_together = ( ( 'server', 'owner' ), ( 'server', 'mumbleid' ) );
|
||||||
verbose_name = _( 'User account' );
|
verbose_name = _( 'User account' );
|
||||||
@@ -418,10 +572,8 @@ class MumbleUser( models.Model ):
|
|||||||
def save( self, dontConfigureMurmur=False ):
|
def save( self, dontConfigureMurmur=False ):
|
||||||
""" Save the settings in this model to Murmur. """
|
""" Save the settings in this model to Murmur. """
|
||||||
if dontConfigureMurmur:
|
if dontConfigureMurmur:
|
||||||
# skip murmur configuration, e.g. because we're inserting models for existing players.
|
|
||||||
return models.Model.save( self );
|
return models.Model.save( self );
|
||||||
|
|
||||||
# Before the record set is saved, update Murmur via controller.
|
|
||||||
ctl = self.server.ctl;
|
ctl = self.server.ctl;
|
||||||
|
|
||||||
if self.owner:
|
if self.owner:
|
||||||
@@ -451,7 +603,6 @@ class MumbleUser( models.Model ):
|
|||||||
# Don't save the users' passwords, we don't need them anyway
|
# Don't save the users' passwords, we don't need them anyway
|
||||||
self.password = '';
|
self.password = '';
|
||||||
|
|
||||||
# Now allow django to save the record set
|
|
||||||
return models.Model.save( self );
|
return models.Model.save( self );
|
||||||
|
|
||||||
def __init__( self, *args, **kwargs ):
|
def __init__( self, *args, **kwargs ):
|
||||||
@@ -461,10 +612,15 @@ class MumbleUser( models.Model ):
|
|||||||
# Admin handlers
|
# Admin handlers
|
||||||
def getAdmin( self ):
|
def getAdmin( self ):
|
||||||
""" Get ACL of root Channel, get the admin group and see if this user is in it. """
|
""" Get ACL of root Channel, get the admin group and see if this user is in it. """
|
||||||
|
if self.mumbleid == -1:
|
||||||
|
return False;
|
||||||
|
else:
|
||||||
return self.server.rootchan.acl.group_has_member( "admin", self.mumbleid );
|
return self.server.rootchan.acl.group_has_member( "admin", self.mumbleid );
|
||||||
|
|
||||||
def setAdmin( self, value ):
|
def setAdmin( self, value ):
|
||||||
""" Set or revoke this user's membership in the admin group on the root channel. """
|
""" Set or revoke this user's membership in the admin group on the root channel. """
|
||||||
|
if self.mumbleid == -1:
|
||||||
|
return False;
|
||||||
if value:
|
if value:
|
||||||
self.server.rootchan.acl.group_add_member( "admin", self.mumbleid );
|
self.server.rootchan.acl.group_add_member( "admin", self.mumbleid );
|
||||||
else:
|
else:
|
||||||
@@ -472,7 +628,8 @@ class MumbleUser( models.Model ):
|
|||||||
self.server.rootchan.acl.save();
|
self.server.rootchan.acl.save();
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
aclAdmin = property( getAdmin, setAdmin, doc="Wrapper around getAdmin/setAdmin (not a database field like isAdmin)" );
|
aclAdmin = property( getAdmin, setAdmin, doc=ugettext_noop('Admin on root channel') );
|
||||||
|
|
||||||
|
|
||||||
# Registration fetching
|
# Registration fetching
|
||||||
def getRegistration( self ):
|
def getRegistration( self ):
|
||||||
@@ -481,38 +638,19 @@ class MumbleUser( models.Model ):
|
|||||||
self._registration = self.server.ctl.getRegistration( self.server.srvid, self.mumbleid );
|
self._registration = self.server.ctl.getRegistration( self.server.srvid, self.mumbleid );
|
||||||
return self._registration;
|
return self._registration;
|
||||||
|
|
||||||
registration = property( getRegistration, doc=getRegistration.__doc__ );
|
registration = property( getRegistration );
|
||||||
|
|
||||||
def getComment( self ):
|
|
||||||
""" Retrieve a user's comment, if any. """
|
|
||||||
if "comment" in self.registration:
|
|
||||||
return self.registration["comment"];
|
|
||||||
else:
|
|
||||||
return None;
|
|
||||||
|
|
||||||
comment = property( getComment, doc=getComment.__doc__ );
|
|
||||||
|
|
||||||
def getHash( self ):
|
|
||||||
""" Retrieve a user's hash, if any. """
|
|
||||||
if "hash" in self.registration:
|
|
||||||
return self.registration["hash"];
|
|
||||||
else:
|
|
||||||
return None;
|
|
||||||
|
|
||||||
hash = property( getHash, doc=getHash.__doc__ );
|
|
||||||
|
|
||||||
# Texture handlers
|
# Texture handlers
|
||||||
|
|
||||||
def getTexture( self ):
|
def getTexture( self ):
|
||||||
""" Get the user texture as a PIL Image. """
|
""" Get the user texture as a PIL Image. """
|
||||||
return self.server.ctl.getTexture(self.server.srvid, self.mumbleid);
|
return self.server.ctl.getTexture(self.server.srvid, self.mumbleid);
|
||||||
|
|
||||||
def setTexture( self, infile ):
|
def setTexture( self, image ):
|
||||||
""" Read an image from the infile and install it as the user's texture. """
|
""" Install the given image as the user's texture. """
|
||||||
self.server.ctl.setTexture(self.server.srvid, self.mumbleid, infile)
|
self.server.ctl.setTexture(self.server.srvid, self.mumbleid, image)
|
||||||
|
|
||||||
texture = property( getTexture, setTexture,
|
texture = property( getTexture, setTexture,
|
||||||
doc="Get the texture as a PIL Image or read from a file (pass the path)."
|
doc="Get the texture as a PIL Image or set the Image as the texture."
|
||||||
);
|
);
|
||||||
|
|
||||||
def hasTexture( self ):
|
def hasTexture( self ):
|
||||||
@@ -530,10 +668,10 @@ class MumbleUser( models.Model ):
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
return reverse( showTexture, kwargs={ 'server': self.server.id, 'userid': self.id } );
|
return reverse( showTexture, kwargs={ 'server': self.server.id, 'userid': self.id } );
|
||||||
|
|
||||||
textureUrl = property( getTextureUrl, doc=getTextureUrl.__doc__ );
|
textureUrl = property( getTextureUrl );
|
||||||
|
|
||||||
|
|
||||||
# Deletion handler
|
# Deletion handler
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def pre_delete_listener( **kwargs ):
|
def pre_delete_listener( **kwargs ):
|
||||||
kwargs['instance'].unregister();
|
kwargs['instance'].unregister();
|
||||||
@@ -546,7 +684,6 @@ class MumbleUser( models.Model ):
|
|||||||
|
|
||||||
|
|
||||||
# "server" field protection
|
# "server" field protection
|
||||||
|
|
||||||
def __setattr__( self, name, value ):
|
def __setattr__( self, name, value ):
|
||||||
if name == 'server':
|
if name == 'server':
|
||||||
if self.id is not None and self.server != value:
|
if self.id is not None and self.server != value:
|
||||||
|
|||||||
28
mumble/templates/mumble/channel.html
Normal file
28
mumble/templates/mumble/channel.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{% comment %}
|
||||||
|
<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
|
||||||
|
{% endcomment %}
|
||||||
|
{% load mumble_extras %}
|
||||||
|
<div class="mumble" style="background-image: url( {{ MEDIA_URL }}/mumble/linie_v.png )">
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/knoten_v.png" alt="" />
|
||||||
|
{% if Channel.linked %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/channel_linked.png" alt="linked channel" />
|
||||||
|
{% else %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/channel.png" alt="channel" />
|
||||||
|
{% endif %}
|
||||||
|
{% if Channel.server.netloc %}
|
||||||
|
<a href="{{ Channel|chanurl:MumbleAccount }}" class="mumble" id="link_{{ Channel.id }}" title="{{ Channel.name }}">
|
||||||
|
{{ Channel.name|trunc:30 }}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="mumble" id="link_{{ Channel.id }}" title="{{ Channel.name }}">
|
||||||
|
{{ Channel.name|trunc:30 }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% for sub in Channel.subchans %}
|
||||||
|
{% if sub.show %}
|
||||||
|
{{ sub|chanview:MumbleAccount }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for player in Channel.players %}{{ player|chanview }}{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
24
mumble/templates/mumble/list.html
Normal file
24
mumble/templates/mumble/list.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{% extends "index.html" %}
|
||||||
|
{% comment %}
|
||||||
|
<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
|
||||||
|
{% endcomment %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load mumble_extras %}
|
||||||
|
{% block Headline %}
|
||||||
|
Configured Mumble Servers
|
||||||
|
{% endblock %}
|
||||||
|
{% block Content %}
|
||||||
|
<div class="rahmen">
|
||||||
|
<ul>
|
||||||
|
{% for mumble in MumbleObjects %}
|
||||||
|
{% if mumble.booted %}
|
||||||
|
<li><a href="{% url mumble.views.show mumble.id %}">{{ mumble.name }}</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li>{{ mumble.name }} (offline)</li>
|
||||||
|
{% endif %}
|
||||||
|
{% empty %}
|
||||||
|
{% trans "No server instances have been configured yet." %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
21
mumble/templates/mumble/mobile_list.html
Normal file
21
mumble/templates/mumble/mobile_list.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{% extends "mobile_index.html" %}
|
||||||
|
{% comment %}
|
||||||
|
<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
|
||||||
|
{% endcomment %}
|
||||||
|
{% load mumble_extras %}
|
||||||
|
{% block Headline %}
|
||||||
|
Configured Mumble Servers
|
||||||
|
{% endblock %}
|
||||||
|
{% block LeftColumn %}
|
||||||
|
<div class="rahmen">
|
||||||
|
<ul>
|
||||||
|
{% for mumble in MumbleObjects %}
|
||||||
|
{% if mumble.booted %}
|
||||||
|
<li><a href="{% url mumble.views.mobile_show mumble.id %}">{{ mumble.name }}</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li>{{ mumble.name }} (offline)</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
12
mumble/templates/mumble/mobile_mumble.html
Normal file
12
mumble/templates/mumble/mobile_mumble.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{% extends "mobile_index.html" %}
|
||||||
|
{% comment %}
|
||||||
|
<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
|
||||||
|
{% endcomment %}
|
||||||
|
{% load mumble_extras %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block Headline %}
|
||||||
|
{{ DBaseObject.name }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block LeftColumn %}
|
||||||
|
{{ DBaseObject|chanview:MumbleAccount }}
|
||||||
|
{% endblock %}
|
||||||
388
mumble/templates/mumble/mumble.html
Normal file
388
mumble/templates/mumble/mumble.html
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
{% extends "index.html" %}
|
||||||
|
{% comment %}
|
||||||
|
<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
|
||||||
|
{% endcomment %}
|
||||||
|
{% load mumble_extras %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block Headline %}
|
||||||
|
{{ DBaseObject.name }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block LeftColumn %}
|
||||||
|
{{ DBaseObject|chanview:MumbleAccount }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block Content %}
|
||||||
|
<noscript>
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}
|
||||||
|
<b>Hint:</b><br />
|
||||||
|
This area is used to display additional information for each channel and player, but requires JavaScript to be
|
||||||
|
displayed correctly. You will not see the detail pages, but you can use all links and forms
|
||||||
|
that are displayed.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
</noscript>
|
||||||
|
<div id="mumble_ext_container"></div>
|
||||||
|
<div id="mumble_motd" class="mumble-ext x-hide-display">
|
||||||
|
<ul>
|
||||||
|
{% if DBaseObject.connecturl %}
|
||||||
|
<li>{% trans "Server Address" %}: <a href="{{ DBaseObject.connecturl }}">{{ DBaseObject.connecturl }}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if DBaseObject.url %}
|
||||||
|
<li>{% trans "Website" %}: {{ DBaseObject.url|urlize }}</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>{% trans "Server version" %}: {{ DBaseObject.version.0 }}.{{ DBaseObject.version.1 }}.{{ DBaseObject.version.2 }}</li>
|
||||||
|
<li><a href="{% url mumble.views.mobile_show DBaseObject.id %}">{% trans "Minimal view" %}</a></li>
|
||||||
|
</ul>
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "Welcome message" %}</legend>
|
||||||
|
{{ DBaseObject.motd|removetags:"script link meta html head body style"|safe }}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div id="mumble_registration" class="mumble-ext">
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<h2>{% trans "Server registration" %}</h2>
|
||||||
|
<form action="{% url mumble.views.show DBaseObject.id %}" method="post">
|
||||||
|
{% if Registered %}
|
||||||
|
{% trans "You are registered on this server" %}.<br />
|
||||||
|
{% else %}
|
||||||
|
{% trans "You do not have an account on this server" %}.<br />
|
||||||
|
{% endif %}
|
||||||
|
<table>
|
||||||
|
{{ RegForm }}
|
||||||
|
</table>
|
||||||
|
<input type="hidden" name="mode" value="reg" />
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans %}
|
||||||
|
<p>You need to be <a href="{{ login_url }}">logged in</a> to be able to register an account on this Mumble server.</p>
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if Registered %}
|
||||||
|
<div id="mumble_texture" class="mumble-ext">
|
||||||
|
<h2>{% trans "User Texture" %}</h2>
|
||||||
|
{% if DBaseObject|mmversion_eq:"1.2.2" %}
|
||||||
|
{% blocktrans %}
|
||||||
|
Sorry, due to a bug in Murmur 1.2.2, displaying and setting the Texture is disabled.
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
{% blocktrans with DBaseObject.id as serverid %}
|
||||||
|
You can upload an image that you would like to use as your user texture here.
|
||||||
|
{% endblocktrans %}<br />
|
||||||
|
<br />
|
||||||
|
{% if MumbleAccount.hasTexture %}
|
||||||
|
{% trans "Your current texture is" %}:<br />
|
||||||
|
<img src="{% url mumble.views.showTexture DBaseObject.id MumbleAccount.id %}" alt="user texture" /><br />
|
||||||
|
{% else %}
|
||||||
|
{% trans "You don't currently have a texture set" %}.<br />
|
||||||
|
{% endif %}
|
||||||
|
<br />
|
||||||
|
{% if DBaseObject|mmversion_lt:"1.2.3" %}
|
||||||
|
{% blocktrans with DBaseObject.id as serverid %}
|
||||||
|
Hint: The texture image <b>needs</b> to be 600x60 in size. If you upload an image with
|
||||||
|
a different size, it will be resized accordingly.<br />
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<form action="{% url mumble.views.show DBaseObject.id %}" method="post" enctype="multipart/form-data">
|
||||||
|
<table>
|
||||||
|
{{ TextureForm }}
|
||||||
|
</table>
|
||||||
|
<input type="hidden" name="mode" value="texture" />
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if CurrentUserIsAdmin %}
|
||||||
|
<div id="mumble_admin" class="mumble-ext">
|
||||||
|
<h2>{% trans "Server administration" %}</h2>
|
||||||
|
<form action="{% url mumble.views.show DBaseObject.id %}" method="post">
|
||||||
|
<table>
|
||||||
|
{{ AdminForm }}
|
||||||
|
</table>
|
||||||
|
<input type="hidden" name="mode" value="admin" />
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for item in ChannelTable %}
|
||||||
|
{% if item.is_player %}
|
||||||
|
<div id="mumble_{{ item.id }}" class="mumble-ext x-hide-display">
|
||||||
|
<h2>{% trans "Player" %} {{ item.name }}</h2>
|
||||||
|
<ul>
|
||||||
|
<li>{% trans "Online since" %}: {{ item.onlinesince|time }}</li>
|
||||||
|
<li>{% trans "Authenticated" %}: {{ item.isAuthed|yesno }}</li>
|
||||||
|
<li>{% trans "Admin" %}: {{ item.isAdmin|yesno }}</li>
|
||||||
|
<li>{% trans "Muted" %}: {{ item.mute|yesno }}</li>
|
||||||
|
<li>{% trans "Deafened" %}: {{ item.deaf|yesno }}</li>
|
||||||
|
<li>{% trans "Muted by self" %}: {{ item.selfMute|yesno }}</li>
|
||||||
|
<li>{% trans "Deafened by self" %}: {{ item.selfDeaf|yesno }}</li>
|
||||||
|
{% if CurrentUserIsAdmin or user.is_staff %}
|
||||||
|
<li>{% trans "IP Address" %}: <acronym title="{{ item.ipaddress }}">{{ item.fqdn }}</acronym></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% if item.mumbleuser and item.mumbleuser.owner %}
|
||||||
|
<h2>{% trans "User" %} {{ item.mumbleuser.owner.username|capfirst }}</h2>
|
||||||
|
<ul>
|
||||||
|
{% if item.mumbleuser.owner.first_name and item.mumbleuser.owner.last_name %}
|
||||||
|
<li>{% trans "Full Name" %}: {{ item.mumbleuser.owner.first_name }} {{ item.mumbleuser.owner.last_name }}</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>{% trans "Admin" %}: {{ item.mumbleuser.owner.is_staff|yesno }}</li>
|
||||||
|
<li>{% trans "Sign-up date" %}: {{ item.mumbleuser.owner.date_joined|date }}</li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% if item.comment %}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "User Comment" %}</legend>
|
||||||
|
{{ item.comment|removetags:"script link meta html head body style"|safe }}
|
||||||
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
|
{% if item.mumbleuser and item.mumbleuser.hasTexture %}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "User Texture" %}</legend>
|
||||||
|
<img src="{% url mumble.views.showTexture DBaseObject.id item.mumbleuser.id %}" alt="user texture" />
|
||||||
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
|
{% if CurrentUserIsAdmin or user.is_staff %}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "Kick user" %}</legend>
|
||||||
|
<form action="{% url mumble.views.show DBaseObject.id %}" method="POST">
|
||||||
|
<input type="hidden" name="mode" value="kick" />
|
||||||
|
<input type="hidden" name="session" value="{{ item.session }}" />
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<label for="inp_reason">{% trans "Reason" %}</label>
|
||||||
|
<input type="text" name="reason" value="" id="inp_reason" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="checkbox" name="ban" value="1" id="inp_ban" />
|
||||||
|
<label for="inp_ban">{% trans "Ban user" %}</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<input type="submit" value="{% trans "Kick user" %}" />
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div id="mumble_{{ item.id }}" class="mumble-ext x-hide-display">
|
||||||
|
<h2>{% trans "Channel" %} {{ item.name }}</h2>
|
||||||
|
{% if CurrentUserIsAdmin or user.is_staff %}
|
||||||
|
{% trans "Channel ID" %}: {{ item.chanid }}<br />
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ item|chanurl:MumbleAccount }}" class="mumble">{% trans "Connect" %}</a>
|
||||||
|
{% if item.description %}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "Channel description" %}</legend>
|
||||||
|
{{ item.description|removetags:"script link meta html head body style"|safe }}
|
||||||
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block HeadTag %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
Ext.onReady( function(){
|
||||||
|
Ext.get( 'mumble_registration' ).addClass( 'x-hide-display' );
|
||||||
|
{% if Registered %}
|
||||||
|
Ext.get( 'mumble_texture' ).addClass( 'x-hide-display' );
|
||||||
|
{% endif %}
|
||||||
|
{% if CurrentUserIsAdmin %}
|
||||||
|
Ext.get( 'mumble_admin' ).addClass( 'x-hide-display' );
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if CurrentUserIsAdmin %}
|
||||||
|
userRecord = Ext.data.Record.create([
|
||||||
|
{ name: 'id', type: 'int' },
|
||||||
|
{ name: 'name', type: 'string' },
|
||||||
|
{ name: 'password', type: 'string' },
|
||||||
|
{ name: 'owner', type: 'int' },
|
||||||
|
{ name: 'admin', type: 'bool' },
|
||||||
|
{ name: 'delete', type: 'bool' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
userAdminStore = new Ext.data.Store({
|
||||||
|
url: '{% url mumble.views.users DBaseObject.id %}',
|
||||||
|
reader: new Ext.data.JsonReader({
|
||||||
|
root: 'objects',
|
||||||
|
fields: userRecord
|
||||||
|
}),
|
||||||
|
autoLoad: true,
|
||||||
|
remoteSort: false
|
||||||
|
});
|
||||||
|
|
||||||
|
adminColumn = new Ext.grid.CheckColumn({
|
||||||
|
header: '{% trans "Admin on root channel" %}',
|
||||||
|
dataIndex: 'admin',
|
||||||
|
width: 50
|
||||||
|
});
|
||||||
|
|
||||||
|
deleteColumn = new Ext.grid.CheckColumn({
|
||||||
|
header: '{% trans "Delete" %}',
|
||||||
|
dataIndex: 'delete',
|
||||||
|
width: 50
|
||||||
|
});
|
||||||
|
|
||||||
|
ownerCombo = new Ext.form.ComboBox({
|
||||||
|
name: 'owner',
|
||||||
|
hiddenName: 'owner_id',
|
||||||
|
forceSelection: true,
|
||||||
|
triggerAction: 'all',
|
||||||
|
valueField: 'uid',
|
||||||
|
displayField: 'uname',
|
||||||
|
store: new Ext.data.Store({
|
||||||
|
url: '{% url mumble.views.djangousers %}',
|
||||||
|
reader: new Ext.data.JsonReader({
|
||||||
|
fields: [ 'uid', 'uname' ],
|
||||||
|
root: 'objects'
|
||||||
|
}),
|
||||||
|
autoLoad: true
|
||||||
|
})
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
var cardpanel = new Ext.Panel({
|
||||||
|
renderTo: 'mumble_ext_container',
|
||||||
|
layout: 'card',
|
||||||
|
id: 'mumble_container',
|
||||||
|
height: 570,
|
||||||
|
activeItem: 0,
|
||||||
|
border: false,
|
||||||
|
items: [ {
|
||||||
|
id: 'mumble_tabpanel',
|
||||||
|
xtype: 'tabpanel',
|
||||||
|
defaults: { autoheight: true },
|
||||||
|
activeTab: {{ DisplayTab }},
|
||||||
|
items: [
|
||||||
|
{ contentEl: 'mumble_motd', title: '{% trans "Server Info" %}', autoScroll: true },
|
||||||
|
{ contentEl: 'mumble_registration', title: '{% trans "Registration" %}', autoScroll: true,
|
||||||
|
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
listeners: {
|
||||||
|
activate: function(){ Ext.fly("id_name").focus() }
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
},
|
||||||
|
{% if CurrentUserIsAdmin %}
|
||||||
|
{ contentEl: 'mumble_admin', title: '{% trans "Administration" %}', autoScroll: true },
|
||||||
|
{% endif %}
|
||||||
|
{% if Registered %}
|
||||||
|
{ contentEl: 'mumble_texture',title: '{% trans "User Texture" %}', autoScroll: true },
|
||||||
|
{% endif %}
|
||||||
|
{% if CurrentUserIsAdmin %}
|
||||||
|
{
|
||||||
|
title: '{% trans "User List" %}',
|
||||||
|
xtype: 'editorgrid',
|
||||||
|
store: userAdminStore,
|
||||||
|
cm: new Ext.grid.ColumnModel( [ {
|
||||||
|
header: '{% trans "name" %}',
|
||||||
|
dataIndex: 'name',
|
||||||
|
sortable: true,
|
||||||
|
editor: new Ext.form.TextField({
|
||||||
|
allowBlank: false
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
header: '{% trans "Account owner" %}',
|
||||||
|
dataIndex: 'owner',
|
||||||
|
editor: ownerCombo,
|
||||||
|
sortable: true,
|
||||||
|
renderer: function( value ){
|
||||||
|
if( value == '' ) return '';
|
||||||
|
items = ownerCombo.store.data.items;
|
||||||
|
for( i = 0; i < items.length; i++ )
|
||||||
|
if( items[i].data.uid == value )
|
||||||
|
return items[i].data.uname;
|
||||||
|
}
|
||||||
|
|
||||||
|
}, adminColumn, {
|
||||||
|
header: '{% trans "Change password" %}',
|
||||||
|
dataIndex: 'password',
|
||||||
|
editor: new Ext.form.TextField({
|
||||||
|
inputType: 'password'
|
||||||
|
}),
|
||||||
|
renderer: function( value ){
|
||||||
|
ret = '';
|
||||||
|
for( i = 0; i < value.length; i++ )
|
||||||
|
ret += '*';
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}, deleteColumn ] ),
|
||||||
|
tbar: [{
|
||||||
|
text: '{% trans "Add" %}',
|
||||||
|
handler : function(){
|
||||||
|
userAdminStore.add( new userRecord( {
|
||||||
|
id: -1,
|
||||||
|
name: 'New User',
|
||||||
|
admin: false,
|
||||||
|
owner: '',
|
||||||
|
password: '',
|
||||||
|
'delete': false
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '{% trans "Save" %}',
|
||||||
|
handler : function(){
|
||||||
|
data = [];
|
||||||
|
for( i = 0; i < userAdminStore.data.items.length; i++ ){
|
||||||
|
rec = userAdminStore.data.items[i];
|
||||||
|
if( rec.dirty ){
|
||||||
|
data.push(rec.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var conn = new Ext.data.Connection();
|
||||||
|
conn.request( {
|
||||||
|
url: userAdminStore.url,
|
||||||
|
params: { data: Ext.encode( data ) },
|
||||||
|
success: function(){
|
||||||
|
for( i = 0; i < userAdminStore.data.items.length; i++ ){
|
||||||
|
rec = userAdminStore.data.items[i];
|
||||||
|
if( rec.data['delete'] == true )
|
||||||
|
userAdminStore.remove( rec );
|
||||||
|
else if( rec.dirty ){
|
||||||
|
rec.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '{% trans "Resync with Murmur" %}',
|
||||||
|
handler: function(){
|
||||||
|
userAdminStore.reload({
|
||||||
|
params: { 'resync': 'true' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
plugins: [ adminColumn, deleteColumn ]
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{% for item in ChannelTable %}
|
||||||
|
{ contentEl: 'mumble_{{ item.id }}', id: 'carditem_{{ item.id }}' }{% if not forloop.last %},{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
Ext.get( 'link_server' ).on( 'click', function( event, target ){
|
||||||
|
cardpanel.layout.setActiveItem( 'mumble_tabpanel' );
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
{% for item in ChannelTable %}
|
||||||
|
Ext.get( 'link_{{ item.id }}' ).on( 'click', function( event, target ){
|
||||||
|
cardpanel.layout.setActiveItem( 'carditem_{{ item.id }}' );
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
{% endfor %}
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
<meta http-equiv="refresh" content="300" />
|
||||||
|
{% endblock %}
|
||||||
14
mumble/templates/mumble/offline.html
Normal file
14
mumble/templates/mumble/offline.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{% extends "index.html" %}
|
||||||
|
{% comment %}
|
||||||
|
<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
|
||||||
|
{% endcomment %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load mumble_extras %}
|
||||||
|
{% block Headline %}
|
||||||
|
{{ DBaseObject.name }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block Content %}
|
||||||
|
<div class="rahmen">
|
||||||
|
{% trans "This server is currently offline." %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
35
mumble/templates/mumble/player.html
Normal file
35
mumble/templates/mumble/player.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{% comment %}
|
||||||
|
<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
|
||||||
|
{% endcomment %}
|
||||||
|
{% load mumble_extras %}
|
||||||
|
{% load i18n %}
|
||||||
|
<div class="mumble" style="background-image: url( {{ MEDIA_URL }}/mumble/linie_v.png )">
|
||||||
|
<span class="mumble">
|
||||||
|
{% if Player.isAuthed %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/authenticated.png" alt="authed" title="{% trans "Authenticated" %}" />
|
||||||
|
{% endif %}
|
||||||
|
{% if Player.mute %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/muted_server.png" alt="muted" title="{% trans "Muted" %}" />
|
||||||
|
{% endif %}
|
||||||
|
{% if Player.suppress %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/muted_suppressed.png" alt="muted" title="{% trans "Suppressed" %}" />
|
||||||
|
{% endif %}
|
||||||
|
{% if Player.deaf %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/deafened_server.png" alt="deafened" title="{% trans "Deafened" %}" />
|
||||||
|
{% endif %}
|
||||||
|
{% if Player.selfMute %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/muted_self.png" alt="self-muted" title="{% trans "Muted by self" %}" />
|
||||||
|
{% endif %}
|
||||||
|
{% if Player.selfDeaf %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/deafened_self.png" alt="self-deafened" title="{% trans "Deafened by self" %}" />
|
||||||
|
{% endif %}
|
||||||
|
{% if Player.hasComment %}
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/comment.png" alt="has comment" title="{% trans "has a User Comment set" %}" />
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/knoten_v.png" alt="" />
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/talking_off.png" alt="Player" />
|
||||||
|
<a id="link_{{ Player.id }}" class="mumble" href="#" title="{{ Player.name }}">{{ Player.name|trunc:30 }}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
14
mumble/templates/mumble/server.html
Normal file
14
mumble/templates/mumble/server.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{% comment %}
|
||||||
|
<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
|
||||||
|
{% endcomment %}
|
||||||
|
{% load mumble_extras %}
|
||||||
|
<div style="margin-left: 20px;">
|
||||||
|
<img src="{{ MEDIA_URL }}/mumble/mumble.16x16.png" alt="server" />
|
||||||
|
<a class="mumble" id="link_server" href="{{ Server|chanurl:MumbleAccount }}">{{ Server.name|trunc:30 }}</a>
|
||||||
|
</div>
|
||||||
|
{% for sub in Server.rootchan.subchans %}
|
||||||
|
{% if sub.show %}
|
||||||
|
{{ sub|chanview:MumbleAccount }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for player in Server.rootchan.players %}{{ player|chanview }}{% endfor %}
|
||||||
14
mumble/templatetags/__init__.py
Normal file
14
mumble/templatetags/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
62
mumble/templatetags/mumble_extras.py
Normal file
62
mumble/templatetags/mumble_extras.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
register = template.Library();
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def trunc( string, maxlen = 50 ):
|
||||||
|
""" converts "a very very extaordinary long text" to "a very very extra... """
|
||||||
|
if len(string) < maxlen:
|
||||||
|
return string;
|
||||||
|
return string[:(maxlen - 3)] + "…";
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def chanview( obj, user = None ):
|
||||||
|
""" renders an mmChannel / mmPlayer object with the correct template """
|
||||||
|
if obj.is_server:
|
||||||
|
return render_to_string( 'mumble/server.html', { 'Server': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } );
|
||||||
|
elif obj.is_channel:
|
||||||
|
return render_to_string( 'mumble/channel.html', { 'Channel': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } );
|
||||||
|
elif obj.is_player:
|
||||||
|
return render_to_string( 'mumble/player.html', { 'Player': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } );
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def chanurl( obj, user ):
|
||||||
|
""" create a connection URL and takes the user's login into account """
|
||||||
|
return obj.getURL( user );
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def mmversion_lt( obj, version ):
|
||||||
|
""" return True if the given Server's version is less than the given version. """
|
||||||
|
return tuple(obj.version[:3]) < tuple([int(v) for v in version.split('.')])
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def mmversion_eq( obj, version ):
|
||||||
|
""" return True if the given Server's version equals the given version. """
|
||||||
|
return tuple(obj.version[:3]) == tuple([int(v) for v in version.split('.')])
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def mmversion_gt( obj, version ):
|
||||||
|
""" return True if the given Server's version is greater than the given version. """
|
||||||
|
return tuple(obj.version[:3]) > tuple([int(v) for v in version.split('.')])
|
||||||
99
mumble/testrunner.py
Normal file
99
mumble/testrunner.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.test.simple import run_tests as django_run_tests
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from murmurenvutils import get_available_versions, run_callback, wait_for_user
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests( test_labels, verbosity=1, interactive=True, extra_tests=[] ):
|
||||||
|
""" Run the Django built in testing framework, but before testing the mumble
|
||||||
|
app, allow Murmur to be set up correctly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not test_labels:
|
||||||
|
test_labels = [ appname.split('.')[-1] for appname in settings.INSTALLED_APPS ];
|
||||||
|
|
||||||
|
# No need to sync any murmur servers for the other apps
|
||||||
|
os.environ['MURMUR_CONNSTR'] = '';
|
||||||
|
|
||||||
|
# The easy way: mumble is not being tested.
|
||||||
|
if "mumble" not in test_labels:
|
||||||
|
return django_run_tests( test_labels, verbosity, interactive, extra_tests );
|
||||||
|
|
||||||
|
# First run everything apart from mumble. mumble will be tested separately, so Murmur
|
||||||
|
# can be set up properly first.
|
||||||
|
failed_tests = 0;
|
||||||
|
|
||||||
|
if len(test_labels) > 1:
|
||||||
|
# only run others if mumble is not the only app to be tested
|
||||||
|
test_labels = list(test_labels);
|
||||||
|
test_labels.remove( "mumble" );
|
||||||
|
failed_tests += django_run_tests( test_labels, verbosity, interactive, extra_tests );
|
||||||
|
|
||||||
|
failed_tests += run_mumble_tests( verbosity, interactive );
|
||||||
|
|
||||||
|
return failed_tests;
|
||||||
|
|
||||||
|
|
||||||
|
def run_mumble_tests( verbosity=1, interactive=True ):
|
||||||
|
|
||||||
|
connstrings = {
|
||||||
|
'DBus': 'net.sourceforge.mumble.murmur',
|
||||||
|
'Ice': 'Meta:tcp -h 127.0.0.1 -p 6502',
|
||||||
|
};
|
||||||
|
|
||||||
|
def django_run_tests_wrapper( process, version ):
|
||||||
|
wr_failed_tests = 0;
|
||||||
|
|
||||||
|
for method in connstrings:
|
||||||
|
# Check if this server is ready to be used with the current method
|
||||||
|
if getattr( process.capabilities, ("has_%s" % method.lower()), False ):
|
||||||
|
print "Testing mumble %s via %s" % ( version, method );
|
||||||
|
|
||||||
|
os.environ['MURMUR_CONNSTR'] = connstrings[method];
|
||||||
|
settings.DEFAULT_CONN = connstrings[method];
|
||||||
|
settings.SLICE_VERSION = [ int(dgt) for dgt in version.split('.') ];
|
||||||
|
|
||||||
|
print "MURMUR_CONNSTR:", os.environ['MURMUR_CONNSTR'];
|
||||||
|
print "DEFAULT_CONN: ", settings.DEFAULT_CONN;
|
||||||
|
print "SLICE_VERSION: ", settings.SLICE_VERSION;
|
||||||
|
|
||||||
|
if not process.capabilities.has_users:
|
||||||
|
print "Waiting for user to connect (60 seconds)."
|
||||||
|
wait_for_user( process, timeout=60 );
|
||||||
|
|
||||||
|
wr_failed_tests += django_run_tests( ('mumble',), verbosity, interactive, [] );
|
||||||
|
else:
|
||||||
|
print "Mumble %s does not support Method %s" % ( version, method );
|
||||||
|
|
||||||
|
return wr_failed_tests;
|
||||||
|
|
||||||
|
failed_tests = 0;
|
||||||
|
|
||||||
|
from mctl import MumbleCtlBase
|
||||||
|
|
||||||
|
for version in get_available_versions():
|
||||||
|
MumbleCtlBase.clearCache();
|
||||||
|
|
||||||
|
run = raw_input( "Run tests for %s? [Y/n] " % version );
|
||||||
|
if run in ('Y', 'y', ''):
|
||||||
|
failed_tests += run_callback( version, django_run_tests_wrapper, version );
|
||||||
|
|
||||||
|
return failed_tests;
|
||||||
208
mumble/tests.py
Normal file
208
mumble/tests.py
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from models import Mumble
|
||||||
|
from utils import ObjectInfo
|
||||||
|
|
||||||
|
|
||||||
|
class InstancesHandling( TestCase ):
|
||||||
|
""" Tests creation, editing and removing of vserver instances. """
|
||||||
|
|
||||||
|
def setUp( self ):
|
||||||
|
# Make sure we always start with a FRESH murmur instance, checking for left-over instances
|
||||||
|
# and deleting them before creating ours.
|
||||||
|
try:
|
||||||
|
self.murmur = Mumble.objects.get( addr="0.0.0.0", port=31337 );
|
||||||
|
except Mumble.DoesNotExist:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.murmur.delete();
|
||||||
|
finally:
|
||||||
|
self.murmur = Mumble( name="#unit testing instance#", addr="0.0.0.0", port=31337 );
|
||||||
|
self.murmur.save();
|
||||||
|
|
||||||
|
def testDefaultConf( self ):
|
||||||
|
conf = self.murmur.ctl.getAllConf( self.murmur.srvid );
|
||||||
|
|
||||||
|
self.assert_( type(conf) == dict );
|
||||||
|
self.assert_( "host" in conf );
|
||||||
|
self.assert_( "port" in conf );
|
||||||
|
self.assert_( "certificate" in conf );
|
||||||
|
self.assert_( "key" in conf );
|
||||||
|
self.assert_( "registerhostname" in conf );
|
||||||
|
self.assert_( "registername" in conf );
|
||||||
|
self.assert_( "channelname" in conf );
|
||||||
|
self.assert_( "username" in conf );
|
||||||
|
self.assert_( "obfuscate" in conf );
|
||||||
|
self.assert_( "defaultchannel" in conf );
|
||||||
|
|
||||||
|
def testAddrPortUnique( self ):
|
||||||
|
try:
|
||||||
|
duplicate = Mumble(
|
||||||
|
name="#another unit testing instance#",
|
||||||
|
addr=self.murmur.addr, port=self.murmur.port,
|
||||||
|
dbus=settings.DEFAULT_CONN
|
||||||
|
);
|
||||||
|
if duplicate.ctl.method == "ICE":
|
||||||
|
import Murmur
|
||||||
|
self.assertRaises( Murmur.ServerFailureException, duplicate.save );
|
||||||
|
elif self.murmur.version[:2] == [ 1, 2 ]:
|
||||||
|
from dbus import DBusException
|
||||||
|
self.assertRaises( DBusException, duplicate.save );
|
||||||
|
else:
|
||||||
|
from sqlite3 import IntegrityError
|
||||||
|
self.assertRaises( IntegrityError, duplicate.save );
|
||||||
|
finally:
|
||||||
|
# make sure the duplicate is removed
|
||||||
|
duplicate.ctl.deleteServer( duplicate.srvid );
|
||||||
|
|
||||||
|
def tearDown( self ):
|
||||||
|
self.murmur.delete();
|
||||||
|
|
||||||
|
|
||||||
|
class DataReading( TestCase ):
|
||||||
|
""" Tests reading data from murmur using the low-level CTL methods. """
|
||||||
|
|
||||||
|
def setUp( self ):
|
||||||
|
# BIG FAT WARNING: This sucks ass, because it assumes the tester has a
|
||||||
|
# Murmur database like the one I have.
|
||||||
|
# I definitely need to prepare Murmur somehow before running these tests.
|
||||||
|
# Just don't yet know how.
|
||||||
|
self.murmur = Mumble.objects.get(id=1);
|
||||||
|
|
||||||
|
|
||||||
|
def testCtlGetChannels( self ):
|
||||||
|
""" Test getChannels() """
|
||||||
|
|
||||||
|
channels = self.murmur.ctl.getChannels( self.murmur.srvid );
|
||||||
|
|
||||||
|
if self.murmur.ctl.method == "ICE":
|
||||||
|
import Murmur
|
||||||
|
self.assertEquals( type( channels[0] ), Murmur.Channel );
|
||||||
|
else:
|
||||||
|
self.assertEquals( type( channels[0] ), ObjectInfo );
|
||||||
|
|
||||||
|
self.assert_( hasattr( channels[0], "id" ) );
|
||||||
|
self.assert_( hasattr( channels[0], "name" ) );
|
||||||
|
self.assert_( hasattr( channels[0], "parent" ) );
|
||||||
|
self.assert_( hasattr( channels[0], "links" ) );
|
||||||
|
|
||||||
|
|
||||||
|
def testCtlGetPlayers( self ):
|
||||||
|
""" Test getPlayers() """
|
||||||
|
|
||||||
|
players = self.murmur.ctl.getPlayers( self.murmur.srvid );
|
||||||
|
|
||||||
|
self.assert_( len(players) > 0 );
|
||||||
|
|
||||||
|
self.assertEquals( type(players), dict );
|
||||||
|
|
||||||
|
for plidx in players:
|
||||||
|
player = players[plidx];
|
||||||
|
|
||||||
|
if self.murmur.ctl.method == "ICE" and self.murmur.version[:2] == ( 1, 2 ):
|
||||||
|
import Murmur
|
||||||
|
self.assertEquals( type( player ), Murmur.User );
|
||||||
|
else:
|
||||||
|
self.assertEquals( type( player ), ObjectInfo );
|
||||||
|
|
||||||
|
self.assert_( hasattr( player, "session" ) );
|
||||||
|
self.assert_( hasattr( player, "mute" ) );
|
||||||
|
self.assert_( hasattr( player, "deaf" ) );
|
||||||
|
self.assert_( hasattr( player, "selfMute" ) );
|
||||||
|
self.assert_( hasattr( player, "selfDeaf" ) );
|
||||||
|
self.assert_( hasattr( player, "channel" ) );
|
||||||
|
self.assert_( hasattr( player, "userid" ) );
|
||||||
|
self.assert_( hasattr( player, "name" ) );
|
||||||
|
self.assert_( hasattr( player, "onlinesecs" ) );
|
||||||
|
self.assert_( hasattr( player, "bytespersec" ) );
|
||||||
|
|
||||||
|
|
||||||
|
def testCtlGetRegisteredPlayers( self ):
|
||||||
|
""" Test getRegistredPlayers() and getRegistration() """
|
||||||
|
|
||||||
|
players = self.murmur.ctl.getRegisteredPlayers( self.murmur.srvid );
|
||||||
|
|
||||||
|
self.assert_( len(players) > 0 );
|
||||||
|
|
||||||
|
self.assertEquals( type(players), dict );
|
||||||
|
|
||||||
|
for plidx in players:
|
||||||
|
player = players[plidx];
|
||||||
|
|
||||||
|
self.assertEquals( type( player ), ObjectInfo );
|
||||||
|
|
||||||
|
self.assert_( hasattr( player, "userid" ) );
|
||||||
|
self.assert_( hasattr( player, "name" ) );
|
||||||
|
self.assert_( hasattr( player, "email" ) );
|
||||||
|
self.assert_( hasattr( player, "pw" ) );
|
||||||
|
|
||||||
|
# compare with getRegistration result
|
||||||
|
reg = self.murmur.ctl.getRegistration( self.murmur.srvid, player.userid );
|
||||||
|
|
||||||
|
self.assertEquals( type( reg ), ObjectInfo );
|
||||||
|
|
||||||
|
self.assert_( hasattr( reg, "userid" ) );
|
||||||
|
self.assert_( hasattr( reg, "name" ) );
|
||||||
|
self.assert_( hasattr( reg, "email" ) );
|
||||||
|
self.assert_( hasattr( reg, "pw" ) );
|
||||||
|
|
||||||
|
self.assertEquals( player.userid, reg.userid );
|
||||||
|
self.assertEquals( player.name, reg.name );
|
||||||
|
self.assertEquals( player.email, reg.email );
|
||||||
|
self.assertEquals( player.pw, reg.pw );
|
||||||
|
|
||||||
|
|
||||||
|
def testCtlGetAcl( self ):
|
||||||
|
""" Test getACL() for the root channel """
|
||||||
|
|
||||||
|
acls, groups, inherit = self.murmur.ctl.getACL( self.murmur.srvid, 0 );
|
||||||
|
|
||||||
|
for rule in acls:
|
||||||
|
if self.murmur.ctl.method == "ICE" and self.murmur.version[:2] == ( 1, 2 ):
|
||||||
|
import Murmur
|
||||||
|
self.assertEquals( type( rule ), Murmur.ACL );
|
||||||
|
else:
|
||||||
|
self.assertEquals( type( rule ), ObjectInfo );
|
||||||
|
|
||||||
|
self.assert_( hasattr( rule, "applyHere" ) );
|
||||||
|
self.assert_( hasattr( rule, "applySubs" ) );
|
||||||
|
self.assert_( hasattr( rule, "inherited" ) );
|
||||||
|
self.assert_( hasattr( rule, "userid" ) );
|
||||||
|
self.assert_( hasattr( rule, "group" ) );
|
||||||
|
self.assert_( hasattr( rule, "allow" ) );
|
||||||
|
self.assert_( hasattr( rule, "deny" ) );
|
||||||
|
|
||||||
|
for grp in groups:
|
||||||
|
if self.murmur.ctl.method == "ICE":
|
||||||
|
import Murmur
|
||||||
|
self.assertEquals( type( grp ), Murmur.Group );
|
||||||
|
else:
|
||||||
|
self.assertEquals( type( grp ), ObjectInfo );
|
||||||
|
|
||||||
|
self.assert_( hasattr( grp, "name" ) );
|
||||||
|
self.assert_( hasattr( grp, "inherited" ) );
|
||||||
|
self.assert_( hasattr( grp, "inherit" ) );
|
||||||
|
self.assert_( hasattr( grp, "inheritable" ) );
|
||||||
|
self.assert_( hasattr( grp, "add" ) );
|
||||||
|
self.assert_( hasattr( grp, "remove" ) );
|
||||||
|
self.assert_( hasattr( grp, "members" ) );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
35
mumble/urls.py
Normal file
35
mumble/urls.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.conf.urls.defaults import patterns
|
||||||
|
|
||||||
|
urlpatterns = patterns(
|
||||||
|
'mumble.views',
|
||||||
|
( r'djangousers', 'djangousers' ),
|
||||||
|
( r'(?P<server>\d+)/users', 'users' ),
|
||||||
|
|
||||||
|
( r'(?P<server>\d+)/(?P<userid>\d+)/texture.png', 'showTexture' ),
|
||||||
|
|
||||||
|
( r'murmur/tree/(?P<server>\d+)', 'mmng_tree' ),
|
||||||
|
( r'mumbleviewer/(?P<server>\d+).xml', 'mumbleviewer_tree_xml' ),
|
||||||
|
( r'mumbleviewer/(?P<server>\d+).json', 'mumbleviewer_tree_json'),
|
||||||
|
|
||||||
|
( r'mobile/(?P<server>\d+)', 'mobile_show' ),
|
||||||
|
( r'mobile/?$', 'mobile_mumbles' ),
|
||||||
|
|
||||||
|
( r'(?P<server>\d+)', 'show' ),
|
||||||
|
( r'$', 'mumbles' ),
|
||||||
|
)
|
||||||
408
mumble/views.py
Normal file
408
mumble/views.py
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
|
||||||
|
*
|
||||||
|
* Mumble-Django is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import simplejson
|
||||||
|
from StringIO import StringIO
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404
|
||||||
|
from django.template import RequestContext
|
||||||
|
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from django.contrib.auth import views as auth_views
|
||||||
|
|
||||||
|
from models import Mumble, MumbleUser
|
||||||
|
from forms import MumbleForm, MumbleUserForm, MumbleUserPasswordForm
|
||||||
|
from forms import MumbleUserLinkForm, MumbleTextureForm, MumbleKickForm
|
||||||
|
|
||||||
|
|
||||||
|
def redir( request ):
|
||||||
|
""" Redirect to the servers list. """
|
||||||
|
if request.META['HTTP_USER_AGENT'].startswith( 'BlackBerry' ) or \
|
||||||
|
"Opera Mobi" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"Opera Mini" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"Windows CE" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"MIDP" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"Palm" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"NetFront" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"Nokia" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"Symbian" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"UP.Browser" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"UP.Link" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"WinWAP" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"Android" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"DoCoMo" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"KDDI-" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"Softbank" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"J-Phone" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"IEMobile" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"iPod" in request.META['HTTP_USER_AGENT'] or \
|
||||||
|
"iPhone" in request.META['HTTP_USER_AGENT']:
|
||||||
|
return HttpResponseRedirect( reverse( mobile_mumbles ) );
|
||||||
|
else:
|
||||||
|
return HttpResponseRedirect( reverse( mumbles ) );
|
||||||
|
|
||||||
|
|
||||||
|
def mobile_mumbles( request ):
|
||||||
|
return mumbles( request, mobile=True );
|
||||||
|
|
||||||
|
|
||||||
|
def mumbles( request, mobile=False ):
|
||||||
|
""" Display a list of all configured Mumble servers, or redirect if only one configured. """
|
||||||
|
mms = Mumble.objects.all().order_by( "name" );
|
||||||
|
|
||||||
|
if len(mms) == 1:
|
||||||
|
return HttpResponseRedirect( reverse(
|
||||||
|
{ False: show, True: mobile_show }[mobile],
|
||||||
|
kwargs={ 'server': mms[0].id, }
|
||||||
|
) );
|
||||||
|
|
||||||
|
return render_to_response(
|
||||||
|
'mumble/%s.html' % { False: 'list', True: 'mobile_list' }[mobile],
|
||||||
|
{ 'MumbleObjects': mms,
|
||||||
|
'MumbleActive': True,
|
||||||
|
},
|
||||||
|
context_instance = RequestContext(request)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
def show( request, server ):
|
||||||
|
""" Display the channel list for the given Server ID.
|
||||||
|
|
||||||
|
This includes not only the channel list itself, but indeed the user registration,
|
||||||
|
server admin and user texture form as well. The template then uses JavaScript
|
||||||
|
to display these forms integrated into the Channel viewer.
|
||||||
|
"""
|
||||||
|
srv = get_object_or_404( Mumble, id=server );
|
||||||
|
if not srv.booted:
|
||||||
|
return render_to_response(
|
||||||
|
'mumble/offline.html',
|
||||||
|
{ 'DBaseObject': srv,
|
||||||
|
'MumbleActive': True,
|
||||||
|
}, context_instance = RequestContext(request) );
|
||||||
|
|
||||||
|
isAdmin = srv.isUserAdmin( request.user );
|
||||||
|
|
||||||
|
# The tab to start on.
|
||||||
|
displayTab = 0;
|
||||||
|
|
||||||
|
if isAdmin:
|
||||||
|
if request.method == 'POST' and "mode" in request.POST and request.POST['mode'] == 'admin':
|
||||||
|
adminform = MumbleForm( request.POST, instance=srv );
|
||||||
|
if adminform.is_valid():
|
||||||
|
adminform.save();
|
||||||
|
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) );
|
||||||
|
else:
|
||||||
|
displayTab = 2;
|
||||||
|
else:
|
||||||
|
adminform = MumbleForm( instance=srv );
|
||||||
|
else:
|
||||||
|
adminform = None;
|
||||||
|
|
||||||
|
registered = False;
|
||||||
|
user = None;
|
||||||
|
|
||||||
|
if request.user.is_authenticated():
|
||||||
|
# Unregistered users may or may not need a password to register.
|
||||||
|
if settings.PROTECTED_MODE and srv.passwd:
|
||||||
|
unregged_user_form = MumbleUserPasswordForm;
|
||||||
|
# Unregistered users may or may not want to link an existing account
|
||||||
|
elif settings.ALLOW_ACCOUNT_LINKING:
|
||||||
|
unregged_user_form = MumbleUserLinkForm;
|
||||||
|
else:
|
||||||
|
unregged_user_form = MumbleUserForm;
|
||||||
|
|
||||||
|
|
||||||
|
if request.method == 'POST' and 'mode' in request.POST and request.POST['mode'] == 'reg':
|
||||||
|
try:
|
||||||
|
user = MumbleUser.objects.get( server=srv, owner=request.user );
|
||||||
|
except MumbleUser.DoesNotExist:
|
||||||
|
regform = unregged_user_form( request.POST );
|
||||||
|
regform.server = srv;
|
||||||
|
if regform.is_valid():
|
||||||
|
model = regform.save( commit=False );
|
||||||
|
model.owner = request.user;
|
||||||
|
model.server = srv;
|
||||||
|
# If we're linking accounts, the change is local only.
|
||||||
|
model.save( dontConfigureMurmur=( "linkacc" in regform.data ) );
|
||||||
|
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) );
|
||||||
|
else:
|
||||||
|
displayTab = 1;
|
||||||
|
else:
|
||||||
|
regform = MumbleUserForm( request.POST, instance=user );
|
||||||
|
regform.server = srv;
|
||||||
|
if regform.is_valid():
|
||||||
|
regform.save();
|
||||||
|
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) );
|
||||||
|
else:
|
||||||
|
displayTab = 1;
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
user = MumbleUser.objects.get( server=srv, owner=request.user );
|
||||||
|
except MumbleUser.DoesNotExist:
|
||||||
|
regform = unregged_user_form();
|
||||||
|
else:
|
||||||
|
regform = MumbleUserForm( instance=user );
|
||||||
|
registered = True;
|
||||||
|
|
||||||
|
if request.method == 'POST' and 'mode' in request.POST and request.POST['mode'] == 'texture' and registered:
|
||||||
|
textureform = MumbleTextureForm( request.POST, request.FILES );
|
||||||
|
if textureform.is_valid():
|
||||||
|
user.setTexture( Image.open( request.FILES['texturefile'] ) );
|
||||||
|
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) );
|
||||||
|
else:
|
||||||
|
textureform = MumbleTextureForm();
|
||||||
|
|
||||||
|
else:
|
||||||
|
regform = None;
|
||||||
|
textureform = None;
|
||||||
|
|
||||||
|
if isAdmin:
|
||||||
|
if request.method == 'POST' and 'mode' in request.POST and request.POST['mode'] == 'kick':
|
||||||
|
kickform = MumbleKickForm( request.POST );
|
||||||
|
if kickform.is_valid():
|
||||||
|
if kickform.cleaned_data["ban"]:
|
||||||
|
srv.banUser( kickform.cleaned_data['session'], kickform.cleaned_data['reason'] );
|
||||||
|
srv.kickUser( kickform.cleaned_data['session'], kickform.cleaned_data['reason'] );
|
||||||
|
|
||||||
|
# ChannelTable is a somewhat misleading name, as it actually contains channels and players.
|
||||||
|
channelTable = [];
|
||||||
|
for cid in srv.channels:
|
||||||
|
if cid != 0 and srv.channels[cid].show:
|
||||||
|
channelTable.append( srv.channels[cid] );
|
||||||
|
for pid in srv.players:
|
||||||
|
channelTable.append( srv.players[pid] );
|
||||||
|
|
||||||
|
show_url = reverse( show, kwargs={ 'server': srv.id } );
|
||||||
|
login_url = reverse( auth_views.login );
|
||||||
|
|
||||||
|
return render_to_response(
|
||||||
|
'mumble/mumble.html',
|
||||||
|
{
|
||||||
|
'login_url': "%s?next=%s" % ( login_url, show_url ),
|
||||||
|
'DBaseObject': srv,
|
||||||
|
'ChannelTable': channelTable,
|
||||||
|
'CurrentUserIsAdmin': isAdmin,
|
||||||
|
'AdminForm': adminform,
|
||||||
|
'RegForm': regform,
|
||||||
|
'TextureForm': textureform,
|
||||||
|
'Registered': registered,
|
||||||
|
'DisplayTab': displayTab,
|
||||||
|
'MumbleActive': True,
|
||||||
|
'MumbleAccount':user,
|
||||||
|
},
|
||||||
|
context_instance = RequestContext(request)
|
||||||
|
);
|
||||||
|
|
||||||
|
def mobile_show( request, server ):
|
||||||
|
""" Display the channel list for the given Server ID. """
|
||||||
|
|
||||||
|
srv = get_object_or_404( Mumble, id=server );
|
||||||
|
|
||||||
|
user = None;
|
||||||
|
if request.user.is_authenticated():
|
||||||
|
try:
|
||||||
|
user = MumbleUser.objects.get( server=srv, owner=request.user );
|
||||||
|
except MumbleUser.DoesNotExist:
|
||||||
|
pass;
|
||||||
|
|
||||||
|
return render_to_response(
|
||||||
|
'mumble/mobile_mumble.html',
|
||||||
|
{
|
||||||
|
'DBaseObject': srv,
|
||||||
|
'MumbleActive': True,
|
||||||
|
'MumbleAccount':user,
|
||||||
|
},
|
||||||
|
context_instance = RequestContext(request)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def showTexture( request, server, userid ):
|
||||||
|
""" Pack the given user's texture into an HttpResponse.
|
||||||
|
|
||||||
|
If userid is none, use the currently logged in User.
|
||||||
|
"""
|
||||||
|
|
||||||
|
srv = get_object_or_404( Mumble, id=int(server) );
|
||||||
|
user = get_object_or_404( MumbleUser, server=srv, id=int(userid) );
|
||||||
|
|
||||||
|
try:
|
||||||
|
img = user.getTexture();
|
||||||
|
except ValueError:
|
||||||
|
raise Http404();
|
||||||
|
else:
|
||||||
|
buf = StringIO();
|
||||||
|
img.save( buf, "PNG" );
|
||||||
|
return HttpResponse( buf.getvalue(), "image/png" );
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def users( request, server ):
|
||||||
|
""" Create a list of MumbleUsers for a given server serialized as a JSON object.
|
||||||
|
|
||||||
|
If the request has a "data" field, evaluate that and update the user records.
|
||||||
|
"""
|
||||||
|
|
||||||
|
srv = get_object_or_404( Mumble, id=int(server) );
|
||||||
|
|
||||||
|
if "resync" in request.POST and request.POST['resync'] == "true":
|
||||||
|
srv.readUsersFromMurmur();
|
||||||
|
|
||||||
|
if not srv.isUserAdmin( request.user ):
|
||||||
|
return HttpResponse(
|
||||||
|
simplejson.dumps({ 'success': False, 'objects': [], 'errormsg': 'Access denied' }),
|
||||||
|
mimetype='text/javascript'
|
||||||
|
);
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
data = simplejson.loads( request.POST['data'] );
|
||||||
|
for record in data:
|
||||||
|
if record['id'] == -1:
|
||||||
|
if record['delete']:
|
||||||
|
continue;
|
||||||
|
mu = MumbleUser( server=srv );
|
||||||
|
else:
|
||||||
|
mu = MumbleUser.objects.get( id=record['id'] );
|
||||||
|
if record['delete']:
|
||||||
|
mu.delete();
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mu.name = record['name'];
|
||||||
|
mu.password = record['password'];
|
||||||
|
if record['owner']:
|
||||||
|
mu.owner = User.objects.get( id=int(record['owner']) );
|
||||||
|
mu.save();
|
||||||
|
mu.aclAdmin = record['admin'];
|
||||||
|
|
||||||
|
users = [];
|
||||||
|
for mu in srv.mumbleuser_set.all():
|
||||||
|
owner = None;
|
||||||
|
if mu.owner is not None:
|
||||||
|
owner = mu.owner.id
|
||||||
|
|
||||||
|
users.append( {
|
||||||
|
'id': mu.id,
|
||||||
|
'name': mu.name,
|
||||||
|
'password': None,
|
||||||
|
'owner': owner,
|
||||||
|
'admin': mu.aclAdmin,
|
||||||
|
} );
|
||||||
|
|
||||||
|
return HttpResponse(
|
||||||
|
simplejson.dumps( { 'success': True, 'objects': users } ),
|
||||||
|
mimetype='text/javascript'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def djangousers( request ):
|
||||||
|
""" Return a list of all Django users' names and IDs. """
|
||||||
|
|
||||||
|
users = [ { 'uid': '', 'uname': '------' } ];
|
||||||
|
for du in User.objects.all().order_by( 'username' ):
|
||||||
|
users.append( {
|
||||||
|
'uid': du.id,
|
||||||
|
'uname': unicode( du ),
|
||||||
|
} );
|
||||||
|
|
||||||
|
return HttpResponse(
|
||||||
|
simplejson.dumps( { 'success': True, 'objects': users } ),
|
||||||
|
mimetype='text/javascript'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
def mmng_tree( request, server ):
|
||||||
|
""" Return a JSON representation of the channel tree suitable for
|
||||||
|
Murmur Manager:
|
||||||
|
http://github.com/cheald/murmur-manager/tree/master/widget/
|
||||||
|
|
||||||
|
To make the client widget query this view, set the URL attribute
|
||||||
|
to "http://<mumble-django base URL>/mumble"
|
||||||
|
"""
|
||||||
|
|
||||||
|
srv = get_object_or_404( Mumble, id=int(server) );
|
||||||
|
|
||||||
|
chanlist = []
|
||||||
|
userlist = []
|
||||||
|
|
||||||
|
for chanid in srv.channels:
|
||||||
|
channel = srv.channels[chanid]
|
||||||
|
|
||||||
|
if channel.parent is not None:
|
||||||
|
parent = channel.parent.chanid
|
||||||
|
else:
|
||||||
|
parent = -1
|
||||||
|
|
||||||
|
chanlist.append({
|
||||||
|
"type": "channel",
|
||||||
|
"id": channel.chanid,
|
||||||
|
"name": channel.name,
|
||||||
|
"parent": parent,
|
||||||
|
"position": channel.position,
|
||||||
|
"state": channel.temporary and "temporary" or "permanent"
|
||||||
|
})
|
||||||
|
|
||||||
|
for sessionid in srv.players:
|
||||||
|
user = srv.players[sessionid]
|
||||||
|
userlist.append({
|
||||||
|
"type": "player",
|
||||||
|
"name": user.name,
|
||||||
|
"channel": user.channel.chanid,
|
||||||
|
"mute": user.mute or user.selfMute or user.suppress,
|
||||||
|
"deaf": user.deaf or user.selfDeaf,
|
||||||
|
"online": user.onlinesecs,
|
||||||
|
"state": "online"
|
||||||
|
})
|
||||||
|
|
||||||
|
if "callback" in request.GET:
|
||||||
|
prefix = request.GET["callback"]
|
||||||
|
else:
|
||||||
|
prefix = ""
|
||||||
|
|
||||||
|
return HttpResponse(
|
||||||
|
prefix + "(" + simplejson.dumps( { 'channels': chanlist, 'users': userlist } ) + ")",
|
||||||
|
mimetype='text/javascript'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
def mumbleviewer_tree_xml( request, server ):
|
||||||
|
""" Get the XML tree from the server and serialize it to the client. """
|
||||||
|
from xml.etree.cElementTree import tostring as xml_to_string
|
||||||
|
srv = get_object_or_404( Mumble, id=int(server) );
|
||||||
|
return HttpResponse(
|
||||||
|
xml_to_string( srv.asMvXml(), encoding='utf-8' ),
|
||||||
|
mimetype='text/xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
def mumbleviewer_tree_json( request, server ):
|
||||||
|
""" Get the Dict from the server and serialize it as JSON to the client. """
|
||||||
|
srv = get_object_or_404( Mumble, id=int(server) );
|
||||||
|
|
||||||
|
if "jsonp_callback" in request.GET:
|
||||||
|
prefix = request.GET["jsonp_callback"]
|
||||||
|
else:
|
||||||
|
prefix = ""
|
||||||
|
|
||||||
|
return HttpResponse(
|
||||||
|
prefix + "(" + simplejson.dumps( srv.asMvJson() ) + ")",
|
||||||
|
mimetype='text/javascript'
|
||||||
|
);
|
||||||
@@ -5,8 +5,8 @@ from reddit.forms import RedditAccountForm
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
class RedditAccountAdmin(admin.ModelAdmin):
|
class RedditAccountAdmin(admin.ModelAdmin):
|
||||||
list_display = ('username', 'user', 'date_created', 'link_karma', 'comment_karma', 'last_update', 'is_valid')
|
list_display = ('username', 'user', 'date_created', 'link_karma', 'comment_karma', 'last_update', 'validated', 'is_valid')
|
||||||
search_fields = ['username', 'user']
|
search_fields = ['username']
|
||||||
|
|
||||||
fields = ('user', 'username')
|
fields = ('user', 'username')
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,39 @@
|
|||||||
import time
|
import time
|
||||||
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
import settings
|
||||||
|
|
||||||
from django_cron import cronScheduler, Job
|
|
||||||
from reddit.models import RedditAccount
|
from reddit.models import RedditAccount
|
||||||
from reddit.api import Inbox
|
from reddit.api import Inbox
|
||||||
|
|
||||||
class UpdateAPIs(Job):
|
class UpdateAPIs():
|
||||||
"""
|
"""
|
||||||
Updates all Reddit API elements in the database
|
Updates all Reddit API elements in the database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# run every 24 hours
|
|
||||||
run_every = 86400
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _logger(self):
|
def _logger(self):
|
||||||
if not hasattr(self, '__logger'):
|
if not hasattr(self, '__logger'):
|
||||||
self.__logger = logging.getLogger(__name__)
|
self.__logger = logging.getLogger(__name__)
|
||||||
return self.__logger
|
return self.__logger
|
||||||
|
|
||||||
|
last_update_delay = 604800
|
||||||
|
|
||||||
def job(self):
|
def job(self):
|
||||||
for acc in RedditAccount.objects.all():
|
delta = datetime.timedelta(seconds=self.last_update_delay)
|
||||||
|
|
||||||
|
print delta
|
||||||
|
self._logger.debug("Updating accounts older than %s" % (datetime.datetime.now() - delta))
|
||||||
|
|
||||||
|
for acc in RedditAccount.objects.filter(last_update__lt=(datetime.datetime.now() - delta)):
|
||||||
|
self._logger.info("Updating %s" % acc.username)
|
||||||
|
|
||||||
|
try:
|
||||||
acc.api_update()
|
acc.api_update()
|
||||||
|
except RedditAccount.DoesNotExist:
|
||||||
|
acc.delete()
|
||||||
|
else:
|
||||||
acc.save()
|
acc.save()
|
||||||
time.sleep(2)
|
time.sleep(.5)
|
||||||
|
|
||||||
|
|
||||||
class APIKeyParser:
|
class APIKeyParser:
|
||||||
@@ -43,22 +53,21 @@ class APIKeyParser:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s:%s" % (self.user_id, self.api_key)
|
return "%s:%s" % (self.user_id, self.api_key)
|
||||||
|
|
||||||
class ProcessInbox(Job):
|
class ProcessValidations():
|
||||||
"""
|
"""
|
||||||
Grabs all Reddit Mail and processes any new applications
|
Grabs all Reddit Mail and processes validations
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def job(self):
|
def job(self):
|
||||||
inbox = Inbox(settings.REDDIT_USER, settings.REDDIT_PASSWORD)
|
inbox = Inbox(settings.REDDIT_USER, settings.REDDIT_PASSWD)
|
||||||
|
|
||||||
for msg in inbox:
|
for msg in inbox:
|
||||||
if not msg.was_comment and msg.new:
|
if not msg.was_comment and msg.new:
|
||||||
try:
|
try:
|
||||||
key = APIKeyParser(msg.body)
|
acc = RedditAccount.objects.get(username__iexact=msg.author)
|
||||||
except:
|
if not acc.validated and msg.body == acc.user.username:
|
||||||
pass
|
acc.validated = True
|
||||||
else:
|
acc.save()
|
||||||
print key.username
|
except RedditAccount.DoesNotExist:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
cronScheduler.register(UpdateAPIs)
|
|
||||||
|
|||||||
1
reddit/evolutions/__init__.py
Normal file
1
reddit/evolutions/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SEQUENCE = ['validation-field']
|
||||||
7
reddit/evolutions/validation-field.py
Normal file
7
reddit/evolutions/validation-field.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from django_evolution.mutations import *
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
MUTATIONS = [
|
||||||
|
AddField('RedditAccount', 'validated', models.BooleanField, initial=False),
|
||||||
|
]
|
||||||
|
|
||||||
@@ -14,11 +14,11 @@ class RedditAccount(models.Model):
|
|||||||
username = models.CharField("Reddit Username", max_length=32, blank=False)
|
username = models.CharField("Reddit Username", max_length=32, blank=False)
|
||||||
reddit_id = models.CharField("Reddit ID", max_length=32)
|
reddit_id = models.CharField("Reddit ID", max_length=32)
|
||||||
|
|
||||||
date_created = models.DateTimeField("Date Created")
|
|
||||||
|
|
||||||
link_karma = models.IntegerField("Link Karma")
|
link_karma = models.IntegerField("Link Karma")
|
||||||
comment_karma = models.IntegerField("Comment Karma")
|
comment_karma = models.IntegerField("Comment Karma")
|
||||||
|
validated = models.BooleanField("Validated")
|
||||||
|
|
||||||
|
date_created = models.DateTimeField("Date Created")
|
||||||
last_update = models.DateTimeField("Last Update from API")
|
last_update = models.DateTimeField("Last Update from API")
|
||||||
|
|
||||||
def api_update(self):
|
def api_update(self):
|
||||||
@@ -32,10 +32,23 @@ class RedditAccount(models.Model):
|
|||||||
self.link_karma = data['link_karma']
|
self.link_karma = data['link_karma']
|
||||||
self.comment_karma = data['comment_karma']
|
self.comment_karma = data['comment_karma']
|
||||||
self.reddit_id = data['id']
|
self.reddit_id = data['id']
|
||||||
self.date_created = datetime.fromtimestamp(data['created_utc'])
|
|
||||||
|
|
||||||
|
self.date_created = datetime.fromtimestamp(data['created_utc'])
|
||||||
self.last_update = datetime.now()
|
self.last_update = datetime.now()
|
||||||
|
|
||||||
|
def recent_posts(self):
|
||||||
|
try:
|
||||||
|
jsondoc = json.load(urllib.urlopen("http://reddit.com/user/%s.json" % self.username))
|
||||||
|
except:
|
||||||
|
raise self.DoesNotExist
|
||||||
|
|
||||||
|
posts = []
|
||||||
|
for item in jsondoc['data']['children']:
|
||||||
|
posts.append(item['data'])
|
||||||
|
|
||||||
|
return posts
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'reddit'
|
app_label = 'reddit'
|
||||||
ordering = ['username']
|
ordering = ['username']
|
||||||
|
|||||||
0
registration/__init__.py
Normal file
0
registration/__init__.py
Normal file
11
registration/admin.py
Normal file
11
registration/admin.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from registration.models import RegistrationProfile
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('__unicode__', 'activation_key_expired')
|
||||||
|
search_fields = ('user__username', 'user__first_name')
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(RegistrationProfile, RegistrationAdmin)
|
||||||
20
registration/cron.py
Normal file
20
registration/cron.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from registration.models import RegistrationProfile
|
||||||
|
|
||||||
|
class RemoveExpiredProfiles():
|
||||||
|
"""
|
||||||
|
Deletes expired profile requests
|
||||||
|
"""
|
||||||
|
|
||||||
|
# run every 2 hours
|
||||||
|
run_every = 7200
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _logger(self):
|
||||||
|
if not hasattr(self, '__logger'):
|
||||||
|
self.__logger = logging.getLogger(__name__)
|
||||||
|
return self.__logger
|
||||||
|
|
||||||
|
def job(self):
|
||||||
|
RegistrationProfile.objects.delete_expired_users()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user