Account Management Implemented.
Account management is now implemented for the GTK UI. Some changes to the core were required since the clients need to know which authentication levels exist, and, to expose account creation, update, and removal to the clients. The best effort is done to try not to compromise the auth file.
This commit is contained in:
parent
6ed3136c8e
commit
43e3fe2a1a
|
@ -2,6 +2,7 @@
|
|||
# authmanager.py
|
||||
#
|
||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2011 Pedro Algarvio <ufs@ufsoft.org>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
|
@ -40,7 +41,7 @@ import logging
|
|||
|
||||
import deluge.component as component
|
||||
import deluge.configmanager as configmanager
|
||||
from deluge.error import BadLoginError, AuthenticationRequired
|
||||
from deluge.error import AuthManagerError, AuthenticationRequired, BadLoginError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -51,21 +52,35 @@ AUTH_LEVEL_ADMIN = 10
|
|||
|
||||
AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
|
||||
|
||||
AUTH_LEVELS_MAPPING = {
|
||||
'NONE': AUTH_LEVEL_NONE,
|
||||
'READONLY': AUTH_LEVEL_READONLY,
|
||||
'DEFAULT': AUTH_LEVEL_NORMAL,
|
||||
'NORMAL': AUTH_LEVEL_DEFAULT,
|
||||
'ADMIN': AUTH_LEVEL_ADMIN
|
||||
}
|
||||
|
||||
AUTH_LEVELS_MAPPING_REVERSE = {}
|
||||
for key, value in AUTH_LEVELS_MAPPING.iteritems():
|
||||
AUTH_LEVELS_MAPPING_REVERSE[value] = key
|
||||
|
||||
class Account(object):
|
||||
__slots__ = ('username', 'password', 'auth_level')
|
||||
def __init__(self, username, password, auth_level):
|
||||
__slots__ = ('username', 'password', 'authlevel')
|
||||
def __init__(self, username, password, authlevel):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.auth_level = auth_level
|
||||
self.authlevel = authlevel
|
||||
|
||||
def data(self, include_private=True):
|
||||
rv = self.__dict__.copy()
|
||||
if not include_private:
|
||||
rv['password'] = ''
|
||||
return rv
|
||||
def data(self):
|
||||
return {
|
||||
'username': self.username,
|
||||
'password': self.password,
|
||||
'authlevel': AUTH_LEVELS_MAPPING_REVERSE[self.authlevel],
|
||||
'authlevel_int': self.authlevel
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return ('<Account username="%(username)s" auth_level=%(auth_level)s>' %
|
||||
return ('<Account username="%(username)s" authlevel=%(authlevel)s>' %
|
||||
self.__dict__)
|
||||
|
||||
|
||||
|
@ -104,52 +119,71 @@ class AuthManager(component.Component):
|
|||
"Username and Password are required.", username
|
||||
)
|
||||
|
||||
self.__test_existing_account(username)
|
||||
if username not in self.__auth:
|
||||
# Let's try to re-load the file.. Maybe it's been updated
|
||||
self.__load_auth_file()
|
||||
if username not in self.__auth:
|
||||
raise BadLoginError("Username does not exist", username)
|
||||
|
||||
if self.__auth[username].password == password:
|
||||
# Return the users auth level
|
||||
return self.__auth[username].auth_level
|
||||
return self.__auth[username].authlevel
|
||||
elif not password and self.__auth[username].password:
|
||||
raise AuthenticationRequired("Password is required", username)
|
||||
else:
|
||||
raise BadLoginError("Password does not match")
|
||||
raise BadLoginError("Password does not match", username)
|
||||
|
||||
def get_known_accounts(self, include_private_data=False):
|
||||
def get_known_accounts(self):
|
||||
"""
|
||||
Returns a list of known deluge usernames.
|
||||
"""
|
||||
self.__load_auth_file()
|
||||
rv = {}
|
||||
for account in self.__auth.items():
|
||||
rv[account.username] = account.data(include_private_data)
|
||||
return rv
|
||||
return [account.data() for account in self.__auth.values()]
|
||||
|
||||
def create_account(self, username, password='', auth_level=AUTH_LEVEL_DEFAULT):
|
||||
def create_account(self, username, password, authlevel):
|
||||
if username in self.__auth:
|
||||
raise Something()
|
||||
self.__create_account(username, password, auth_level)
|
||||
raise AuthManagerError("Username in use.", username)
|
||||
try:
|
||||
self.__auth[username] = Account(username, password,
|
||||
AUTH_LEVELS_MAPPING[authlevel])
|
||||
self.write_auth_file()
|
||||
return True
|
||||
except Exception, err:
|
||||
log.exception(err)
|
||||
raise err
|
||||
|
||||
def update_account(self, username, password='', auth_level=AUTH_LEVEL_DEFAULT):
|
||||
if username in self.__auth:
|
||||
raise Something()
|
||||
self.__create_account(username, password, auth_level)
|
||||
def update_account(self, username, password, authlevel):
|
||||
if username not in self.__auth:
|
||||
raise AuthManagerError("Username not known", username)
|
||||
try:
|
||||
self.__auth[username].username = username
|
||||
self.__auth[username].password = password
|
||||
self.__auth[username].authlevel = AUTH_LEVELS_MAPPING[authlevel]
|
||||
self.write_auth_file()
|
||||
return True
|
||||
except Exception, err:
|
||||
log.exception(err)
|
||||
raise err
|
||||
|
||||
def remove_account(self, username):
|
||||
if username in self.__auth:
|
||||
raise Something()
|
||||
if username not in self.__auth:
|
||||
raise AuthManagerError("Username not known", username)
|
||||
elif username == component.get("RPCServer").get_session_user():
|
||||
raise AuthManagerError(
|
||||
"You cannot delete your own account while logged in!", username
|
||||
)
|
||||
|
||||
del self.__auth[username]
|
||||
self.write_auth_file()
|
||||
if component.get("RPCServer").get_session_user() == username:
|
||||
# Force a client logout by the server
|
||||
component.get("RPCServer").logout_current_session()
|
||||
return True
|
||||
|
||||
def write_auth_file(self):
|
||||
old_auth_file = configmanager.get_config_dir("auth")
|
||||
new_auth_file = old_auth_file + '.new'
|
||||
fd = open(new_auth_file, "w")
|
||||
for account in self.__auth.items():
|
||||
for account in self.__auth.values():
|
||||
fd.write(
|
||||
"%(username)s:%(password)s:%(auth_level)s\n" % account.__dict__
|
||||
"%(username)s:%(password)s:%(authlevel_int)s\n" % account.data()
|
||||
)
|
||||
fd.flush()
|
||||
os.fsync(fd.fileno())
|
||||
|
@ -157,18 +191,6 @@ class AuthManager(component.Component):
|
|||
os.rename(new_auth_file, old_auth_file)
|
||||
self.__load_auth_file()
|
||||
|
||||
def __add_account(self, username, password, auth_level):
|
||||
self.__auth[username] = Account(username, password, auth_level)
|
||||
self.write_auth_file()
|
||||
|
||||
def __test_existing_account(self, username):
|
||||
if username not in self.__auth:
|
||||
# Let's try to re-load the file.. Maybe it's been updated
|
||||
self.__load_auth_file()
|
||||
if username not in self.__auth:
|
||||
raise BadLoginError("Username does not exist")
|
||||
return True
|
||||
|
||||
def __create_localclient_account(self):
|
||||
"""
|
||||
Returns the string.
|
||||
|
@ -217,23 +239,27 @@ class AuthManager(component.Component):
|
|||
log.warning("Your auth entry for %s contains no auth level, "
|
||||
"using AUTH_LEVEL_DEFAULT(%s)..", username,
|
||||
AUTH_LEVEL_DEFAULT)
|
||||
auth_level = AUTH_LEVEL_DEFAULT
|
||||
authlevel = AUTH_LEVEL_DEFAULT
|
||||
elif len(lsplit) == 3:
|
||||
username, password, auth_level = lsplit
|
||||
username, password, authlevel = lsplit
|
||||
else:
|
||||
log.error("Your auth file is malformed: Incorrect number of fields!")
|
||||
log.error("Your auth file is malformed: "
|
||||
"Incorrect number of fields!")
|
||||
continue
|
||||
|
||||
username = username.strip()
|
||||
password = password.strip()
|
||||
try:
|
||||
auth_level = int(auth_level)
|
||||
authlevel = int(authlevel)
|
||||
except ValueError:
|
||||
try:
|
||||
authlevel = AUTH_LEVELS_MAPPING[authlevel]
|
||||
except KeyError:
|
||||
log.error("Your auth file is malformed: %r is not a valid auth "
|
||||
"level" % auth_level)
|
||||
"level" % authlevel)
|
||||
continue
|
||||
|
||||
self.__auth[username] = Account(username, password, auth_level)
|
||||
self.__auth[username] = Account(username, password, authlevel)
|
||||
|
||||
if "localclient" not in self.__auth:
|
||||
self.__create_localclient_account()
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# core.py
|
||||
#
|
||||
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2011 Pedro Algarvio <ufs@ufsoft.org>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
|
@ -43,8 +44,6 @@ import threading
|
|||
import tempfile
|
||||
from urlparse import urljoin
|
||||
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.internet.task import LoopingCall
|
||||
import twisted.web.client
|
||||
import twisted.web.error
|
||||
|
||||
|
@ -55,7 +54,8 @@ import deluge.common
|
|||
import deluge.component as component
|
||||
from deluge.event import *
|
||||
from deluge.error import *
|
||||
from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AUTH_LEVEL_DEFAULT
|
||||
from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AUTH_LEVEL_NONE
|
||||
from deluge.core.authmanager import AUTH_LEVELS_MAPPING, AUTH_LEVELS_MAPPING_REVERSE
|
||||
from deluge.core.torrentmanager import TorrentManager
|
||||
from deluge.core.pluginmanager import PluginManager
|
||||
from deluge.core.alertmanager import AlertManager
|
||||
|
@ -77,7 +77,8 @@ class Core(component.Component):
|
|||
log.info("Starting libtorrent %s session..", lt.version)
|
||||
|
||||
# Create the client fingerprint
|
||||
version = [int(value.split("-")[0]) for value in deluge.common.get_version().split(".")]
|
||||
version = [int(value.split("-")[0]) for value in
|
||||
deluge.common.get_version().split(".")]
|
||||
while len(version) < 4:
|
||||
version.append(0)
|
||||
|
||||
|
@ -147,16 +148,16 @@ class Core(component.Component):
|
|||
def __save_session_state(self):
|
||||
"""Saves the libtorrent session state"""
|
||||
try:
|
||||
open(deluge.configmanager.get_config_dir("session.state"), "wb").write(
|
||||
lt.bencode(self.session.state()))
|
||||
session_state = deluge.configmanager.get_config_dir("session.state")
|
||||
open(session_state, "wb").write(lt.bencode(self.session.state()))
|
||||
except Exception, e:
|
||||
log.warning("Failed to save lt state: %s", e)
|
||||
|
||||
def __load_session_state(self):
|
||||
"""Loads the libtorrent session state"""
|
||||
try:
|
||||
self.session.load_state(lt.bdecode(
|
||||
open(deluge.configmanager.get_config_dir("session.state"), "rb").read()))
|
||||
session_state = deluge.configmanager.get_config_dir("session.state")
|
||||
self.session.load_state(lt.bdecode(open(session_state, "rb").read()))
|
||||
except Exception, e:
|
||||
log.warning("Failed to load lt state: %s", e)
|
||||
|
||||
|
@ -212,7 +213,9 @@ class Core(component.Component):
|
|||
log.exception(e)
|
||||
|
||||
try:
|
||||
torrent_id = self.torrentmanager.add(filedump=filedump, options=options, filename=filename)
|
||||
torrent_id = self.torrentmanager.add(
|
||||
filedump=filedump, options=options, filename=filename
|
||||
)
|
||||
except Exception, e:
|
||||
log.error("There was an error adding the torrent file %s", filename)
|
||||
log.exception(e)
|
||||
|
@ -245,16 +248,23 @@ class Core(component.Component):
|
|||
os.remove(filename)
|
||||
except Exception, e:
|
||||
log.warning("Couldn't remove temp file: %s", e)
|
||||
return self.add_torrent_file(filename, base64.encodestring(data), options)
|
||||
return self.add_torrent_file(
|
||||
filename, base64.encodestring(data), options
|
||||
)
|
||||
|
||||
def on_download_fail(failure):
|
||||
if failure.check(twisted.web.error.PageRedirect):
|
||||
new_url = urljoin(url, failure.getErrorMessage().split(" to ")[1])
|
||||
result = download_file(new_url, tempfile.mkstemp()[1], headers=headers, force_filename=True)
|
||||
result = download_file(
|
||||
new_url, tempfile.mkstemp()[1], headers=headers,
|
||||
force_filename=True
|
||||
)
|
||||
result.addCallbacks(on_download_success, on_download_fail)
|
||||
elif failure.check(twisted.web.client.PartialDownloadError):
|
||||
result = download_file(url, tempfile.mkstemp()[1], headers=headers, force_filename=True,
|
||||
allow_compression=False)
|
||||
result = download_file(
|
||||
url, tempfile.mkstemp()[1], headers=headers,
|
||||
force_filename=True, allow_compression=False
|
||||
)
|
||||
result.addCallbacks(on_download_success, on_download_fail)
|
||||
else:
|
||||
# Log the error and pass the failure onto the client
|
||||
|
@ -263,7 +273,9 @@ class Core(component.Component):
|
|||
result = failure
|
||||
return result
|
||||
|
||||
d = download_file(url, tempfile.mkstemp()[1], headers=headers, force_filename=True)
|
||||
d = download_file(
|
||||
url, tempfile.mkstemp()[1], headers=headers, force_filename=True
|
||||
)
|
||||
d.addCallbacks(on_download_success, on_download_fail)
|
||||
return d
|
||||
|
||||
|
@ -829,9 +841,22 @@ class Core(component.Component):
|
|||
"""
|
||||
return lt.version
|
||||
|
||||
@export(AUTH_LEVEL_DEFAULT)
|
||||
@export(AUTH_LEVEL_ADMIN)
|
||||
def get_known_accounts(self):
|
||||
auth_level = component.get("RPCServer").get_session_auth_level()
|
||||
return self.authmanager.get_known_accounts(
|
||||
include_private_data=(auth_level==AUTH_LEVEL_ADMIN)
|
||||
)
|
||||
return self.authmanager.get_known_accounts()
|
||||
|
||||
@export(AUTH_LEVEL_NONE)
|
||||
def get_auth_levels_mappings(self):
|
||||
return (AUTH_LEVELS_MAPPING, AUTH_LEVELS_MAPPING_REVERSE)
|
||||
|
||||
@export(AUTH_LEVEL_ADMIN)
|
||||
def create_account(self, username, password, authlevel):
|
||||
return self.authmanager.create_account(username, password, authlevel)
|
||||
|
||||
@export(AUTH_LEVEL_ADMIN)
|
||||
def update_account(self, username, password, authlevel):
|
||||
return self.authmanager.update_account(username, password, authlevel)
|
||||
|
||||
@export(AUTH_LEVEL_ADMIN)
|
||||
def remove_account(self, username):
|
||||
return self.authmanager.remove_account(username)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# error.py
|
||||
#
|
||||
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2011 Pedro Algarvio <ufs@ufsoft.org>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
|
@ -52,10 +53,9 @@ class InvalidPathError(DelugeError):
|
|||
class NotAuthorizedError(DelugeError):
|
||||
pass
|
||||
|
||||
class BadLoginError(DelugeError):
|
||||
pass
|
||||
|
||||
class AuthenticationRequired(BadLoginError):
|
||||
class _UsernameBasedException(DelugeError):
|
||||
|
||||
def _get_message(self):
|
||||
return self._message
|
||||
def _set_message(self, message):
|
||||
|
@ -70,9 +70,17 @@ class AuthenticationRequired(BadLoginError):
|
|||
username = property(_get_username, _set_username)
|
||||
del _get_username, _set_username
|
||||
|
||||
|
||||
def __init__(self, message, username):
|
||||
super(AuthenticationRequired, self).__init__(message)
|
||||
super(_UsernameBasedException, self).__init__(message)
|
||||
self.message = message
|
||||
self.username = username
|
||||
|
||||
|
||||
class BadLoginError(_UsernameBasedException):
|
||||
pass
|
||||
|
||||
class AuthenticationRequired(BadLoginError):
|
||||
pass
|
||||
|
||||
class AuthManagerError(_UsernameBasedException):
|
||||
pass
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# client.py
|
||||
#
|
||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2011 Pedro Algarvio <ufs@ufsoft.org>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
|
@ -76,6 +77,20 @@ class DelugeRPCError(object):
|
|||
self.exception_msg = exception_msg
|
||||
self.traceback = traceback
|
||||
|
||||
def logable(self):
|
||||
# Create a delugerpcrequest to print out a nice RPCRequest string
|
||||
r = DelugeRPCRequest()
|
||||
r.method = self.method
|
||||
r.args = self.args
|
||||
r.kwargs = self.kwargs
|
||||
msg = "RPCError Message Received!"
|
||||
msg += "\n" + "-" * 80
|
||||
msg += "\n" + "RPCRequest: " + r.__repr__()
|
||||
msg += "\n" + "-" * 80
|
||||
msg += "\n" + self.traceback + "\n" + self.exception_type + ": " + self.exception_msg
|
||||
msg += "\n" + "-" * 80
|
||||
return msg
|
||||
|
||||
class DelugeRPCRequest(object):
|
||||
"""
|
||||
This object is created whenever there is a RPCRequest to be sent to the
|
||||
|
@ -118,6 +133,7 @@ class DelugeRPCRequest(object):
|
|||
return (self.request_id, self.method, self.args, self.kwargs)
|
||||
|
||||
class DelugeRPCProtocol(Protocol):
|
||||
|
||||
def connectionMade(self):
|
||||
self.__rpc_requests = {}
|
||||
self.__buffer = None
|
||||
|
@ -273,6 +289,9 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
self.disconnect_deferred = None
|
||||
self.disconnect_callback = None
|
||||
|
||||
self.auth_levels_mapping = None
|
||||
self.auth_levels_mapping_reverse = None
|
||||
|
||||
def connect(self, host, port):
|
||||
"""
|
||||
Connects to a daemon at host:port
|
||||
|
@ -402,21 +421,8 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
except:
|
||||
pass
|
||||
|
||||
# Get the DelugeRPCError object from the error_data
|
||||
error = error_data.value
|
||||
|
||||
# Create a delugerpcrequest to print out a nice RPCRequest string
|
||||
r = DelugeRPCRequest()
|
||||
r.method = error.method
|
||||
r.args = error.args
|
||||
r.kwargs = error.kwargs
|
||||
msg = "RPCError Message Received!"
|
||||
msg += "\n" + "-" * 80
|
||||
msg += "\n" + "RPCRequest: " + r.__repr__()
|
||||
msg += "\n" + "-" * 80
|
||||
msg += "\n" + error.traceback + "\n" + error.exception_type + ": " + error.exception_msg
|
||||
msg += "\n" + "-" * 80
|
||||
log.error(msg)
|
||||
if error_data.value.exception_type != 'AuthManagerError':
|
||||
log.error(error_data.value.logable())
|
||||
return error_data
|
||||
|
||||
def __on_connect(self, result):
|
||||
|
@ -452,13 +458,24 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
self.authentication_level = result
|
||||
# We need to tell the daemon what events we're interested in receiving
|
||||
if self.__factory.event_handlers:
|
||||
self.call("daemon.set_event_interest", self.__factory.event_handlers.keys())
|
||||
self.call("daemon.set_event_interest",
|
||||
self.__factory.event_handlers.keys())
|
||||
|
||||
self.call("core.get_auth_levels_mappings").addCallback(
|
||||
self.__on_auth_levels_mappings
|
||||
)
|
||||
|
||||
self.login_deferred.callback(result)
|
||||
|
||||
def __on_login_fail(self, result):
|
||||
log.debug("_on_login_fail(): %s", result)
|
||||
self.login_deferred.errback(result)
|
||||
|
||||
def __on_auth_levels_mappings(self, result):
|
||||
auth_levels_mapping, auth_levels_mapping_reverse = result
|
||||
self.auth_levels_mapping = auth_levels_mapping
|
||||
self.auth_levels_mapping_reverse = auth_levels_mapping_reverse
|
||||
|
||||
def set_disconnect_callback(self, cb):
|
||||
"""
|
||||
Set a function to be called when the connection to the daemon is lost
|
||||
|
@ -481,9 +498,13 @@ class DaemonClassicProxy(DaemonProxy):
|
|||
self.host = "localhost"
|
||||
self.port = 58846
|
||||
# Running in classic mode, it's safe to import auth level
|
||||
from deluge.core.authmanager import AUTH_LEVEL_ADMIN
|
||||
from deluge.core.authmanager import (AUTH_LEVEL_ADMIN,
|
||||
AUTH_LEVELS_MAPPING,
|
||||
AUTH_LEVELS_MAPPING_REVERSE)
|
||||
self.username = "localclient"
|
||||
self.authentication_level = AUTH_LEVEL_ADMIN
|
||||
self.auth_levels_mapping = AUTH_LEVELS_MAPPING
|
||||
self.auth_levels_mapping_reverse = AUTH_LEVELS_MAPPING_REVERSE
|
||||
# Register the event handlers
|
||||
for event in event_handlers:
|
||||
for handler in event_handlers[event]:
|
||||
|
@ -776,5 +797,13 @@ class Client(object):
|
|||
"""
|
||||
return self._daemon_proxy.authentication_level
|
||||
|
||||
@property
|
||||
def auth_levels_mapping(self):
|
||||
return self._daemon_proxy.auth_levels_mapping
|
||||
|
||||
@property
|
||||
def auth_levels_mapping_reverse(self):
|
||||
return self._daemon_proxy.auth_levels_mapping_reverse
|
||||
|
||||
# This is the object clients will use
|
||||
client = Client()
|
||||
|
|
|
@ -176,7 +176,7 @@ class ErrorDialog(BaseDialog):
|
|||
details = tb
|
||||
|
||||
if details:
|
||||
self.set_default_size(500, 400)
|
||||
self.set_default_size(600, 400)
|
||||
textview = gtk.TextView()
|
||||
textview.set_editable(False)
|
||||
textview.get_buffer().set_text(details)
|
||||
|
@ -245,3 +245,87 @@ class AuthenticationDialog(BaseDialog):
|
|||
|
||||
def on_password_activate(self, widget):
|
||||
self.response(gtk.RESPONSE_OK)
|
||||
|
||||
class AccountDialog(BaseDialog):
|
||||
def __init__(self, username=None, password=None, authlevel=None,
|
||||
levels_mapping=None, parent=None):
|
||||
if username:
|
||||
super(AccountDialog, self).__init__(
|
||||
_("Edit Account"),
|
||||
_("Edit existing account"),
|
||||
gtk.STOCK_DIALOG_INFO,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_APPLY, gtk.RESPONSE_OK),
|
||||
parent)
|
||||
else:
|
||||
super(AccountDialog, self).__init__(
|
||||
_("New Account"),
|
||||
_("Create a new account"),
|
||||
gtk.STOCK_DIALOG_INFO,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_ADD, gtk.RESPONSE_OK),
|
||||
parent)
|
||||
|
||||
self.levels_mapping = levels_mapping
|
||||
|
||||
table = gtk.Table(2, 3, False)
|
||||
self.username_label = gtk.Label()
|
||||
self.username_label.set_markup(_("<b>Username:</b>"))
|
||||
self.username_label.set_alignment(1.0, 0.5)
|
||||
self.username_label.set_padding(5, 5)
|
||||
self.username_entry = gtk.Entry()
|
||||
table.attach(self.username_label, 0, 1, 0, 1)
|
||||
table.attach(self.username_entry, 1, 2, 0, 1)
|
||||
|
||||
self.authlevel_label = gtk.Label()
|
||||
self.authlevel_label.set_markup(_("<b>Authentication Level:</b>"))
|
||||
self.authlevel_label.set_alignment(1.0, 0.5)
|
||||
self.authlevel_label.set_padding(5, 5)
|
||||
|
||||
self.authlevel_combo = gtk.combo_box_new_text()
|
||||
active_idx = None
|
||||
for idx, level in enumerate(levels_mapping.keys()):
|
||||
self.authlevel_combo.append_text(level)
|
||||
if authlevel and authlevel==level:
|
||||
active_idx = idx
|
||||
elif not authlevel and level == 'DEFAULT':
|
||||
active_idx = idx
|
||||
|
||||
print 'aidx', active_idx
|
||||
if active_idx is not None:
|
||||
self.authlevel_combo.set_active(active_idx)
|
||||
|
||||
table.attach(self.authlevel_label, 0, 1, 1, 2)
|
||||
table.attach(self.authlevel_combo, 1, 2, 1, 2)
|
||||
|
||||
self.password_label = gtk.Label()
|
||||
self.password_label.set_markup(_("<b>Password:</b>"))
|
||||
self.password_label.set_alignment(1.0, 0.5)
|
||||
self.password_label.set_padding(5, 5)
|
||||
self.password_entry = gtk.Entry()
|
||||
self.password_entry.set_visibility(False)
|
||||
table.attach(self.password_label, 0, 1, 2, 3)
|
||||
table.attach(self.password_entry, 1, 2, 2, 3)
|
||||
|
||||
self.vbox.pack_start(table, False, False, padding=5)
|
||||
if username:
|
||||
self.username_entry.set_text(username)
|
||||
self.username_entry.set_editable(False)
|
||||
else:
|
||||
self.set_focus(self.username_entry)
|
||||
|
||||
if password:
|
||||
self.password_entry.set_text(username)
|
||||
|
||||
self.show_all()
|
||||
|
||||
def get_username(self):
|
||||
return self.username_entry.get_text()
|
||||
|
||||
def get_password(self):
|
||||
return self.password_entry.get_text()
|
||||
|
||||
def get_authlevel(self):
|
||||
combobox = self.authlevel_combo
|
||||
level = combobox.get_model()[combobox.get_active()][0]
|
||||
return level
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<glade-interface>
|
||||
<!-- interface-requires gtk+ 2.12 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
|
@ -11,13 +11,11 @@
|
|||
<property name="default_height">530</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="has_separator">False</property>
|
||||
<signal name="delete_event" handler="on_pref_dialog_delete_event"/>
|
||||
<child internal-child="vbox">
|
||||
<widget class="GtkVBox" id="dialog-vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<widget class="GtkHPaned" id="hpaned1">
|
||||
|
@ -50,7 +48,7 @@
|
|||
<property name="show_tabs">False</property>
|
||||
<property name="scrollable">True</property>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<widget class="GtkScrolledWindow" id="DownloadsScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -66,7 +64,6 @@
|
|||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label21">
|
||||
<property name="visible">True</property>
|
||||
|
@ -294,7 +291,7 @@
|
|||
<widget class="GtkEntry" id="entry_torrents_path">
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char">●</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
|
@ -501,7 +498,7 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<widget class="GtkScrolledWindow" id="NetworkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -517,7 +514,6 @@
|
|||
<widget class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label22">
|
||||
<property name="visible">True</property>
|
||||
|
@ -557,7 +553,6 @@
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox2">
|
||||
<property name="visible">True</property>
|
||||
|
@ -755,7 +750,6 @@
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox25">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<widget class="GtkCheckButton" id="chk_random_outgoing_ports">
|
||||
|
@ -880,7 +874,7 @@
|
|||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">Enter the IP address of the interface to listen for incoming bittorrent connections on. Leave this empty if you want to use the default.</property>
|
||||
<property name="max_length">60</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="width_chars">30</property>
|
||||
</widget>
|
||||
<packing>
|
||||
|
@ -927,7 +921,6 @@
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox4">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1127,7 +1120,6 @@
|
|||
<widget class="GtkVBox" id="vbox10">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1159,7 +1151,6 @@
|
|||
<widget class="GtkVBox" id="vbox12">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkComboBox" id="combo_encin">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1193,7 +1184,6 @@ Either</property>
|
|||
<widget class="GtkVBox" id="vbox15">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox15">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1291,7 +1281,7 @@ Disabled</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow3">
|
||||
<widget class="GtkScrolledWindow" id="BandwidthScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -1307,7 +1297,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox7">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label23">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1347,7 +1336,6 @@ Disabled</property>
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox21">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<widget class="GtkTable" id="table1">
|
||||
|
@ -1361,7 +1349,7 @@ Disabled</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9999 1 10 0</property>
|
||||
<property name="adjustment">0 -1 9999 1 10 0</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
|
@ -1377,7 +1365,7 @@ Disabled</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9999 1 10 0</property>
|
||||
<property name="adjustment">0 -1 9999 1 10 0</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
|
@ -1456,7 +1444,7 @@ Disabled</property>
|
|||
<property name="tooltip" translatable="yes">The maximum number of connections allowed. Set -1 for unlimited.</property>
|
||||
<property name="max_length">4</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 0</property>
|
||||
<property name="adjustment">0 -1 9000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
|
@ -1488,7 +1476,7 @@ Disabled</property>
|
|||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="tooltip" translatable="yes">The maximum download speed for all torrents. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 60000 1 10 0</property>
|
||||
<property name="adjustment">0 -1 60000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
|
@ -1507,7 +1495,7 @@ Disabled</property>
|
|||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum upload speed for all torrents. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 60000 1 10 0</property>
|
||||
<property name="adjustment">0 -1 60000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
|
@ -1526,7 +1514,7 @@ Disabled</property>
|
|||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum upload slots for all torrents. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 0</property>
|
||||
<property name="adjustment">0 -1 9000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
|
@ -1631,7 +1619,7 @@ Disabled</property>
|
|||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum upload slots per torrent. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 0</property>
|
||||
<property name="adjustment">0 -1 9000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
|
@ -1650,7 +1638,7 @@ Disabled</property>
|
|||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum number of connections per torrent. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 0</property>
|
||||
<property name="adjustment">0 -1 9000 1 10 0</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
|
@ -1716,7 +1704,7 @@ Disabled</property>
|
|||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum number of connections per torrent. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 0</property>
|
||||
<property name="adjustment">0 -1 9000 1 10 0</property>
|
||||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
|
@ -1734,7 +1722,7 @@ Disabled</property>
|
|||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">The maximum number of connections per torrent. Set -1 for unlimited.</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">-1 -1 9000 1 10 0</property>
|
||||
<property name="adjustment">0 -1 9000 1 10 0</property>
|
||||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
|
@ -1790,7 +1778,7 @@ Disabled</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow4">
|
||||
<widget class="GtkScrolledWindow" id="InterfaceScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -1806,7 +1794,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox8">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label25">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1892,7 +1879,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox27">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkCheckButton" id="chk_show_rate_in_title">
|
||||
<property name="label" translatable="yes">Show session speed in titlebar</property>
|
||||
|
@ -1943,7 +1929,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox5">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkCheckButton" id="chk_show_dialog">
|
||||
<property name="label" translatable="yes">Always show</property>
|
||||
|
@ -2013,7 +1998,6 @@ Disabled</property>
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox17">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkCheckButton" id="chk_use_tray">
|
||||
<property name="label" translatable="yes">Enable system tray icon</property>
|
||||
|
@ -2196,7 +2180,7 @@ Disabled</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow7">
|
||||
<widget class="GtkScrolledWindow" id="OtherScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -2212,7 +2196,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox16">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label40">
|
||||
<property name="visible">True</property>
|
||||
|
@ -2254,7 +2237,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox18">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment36">
|
||||
<property name="visible">True</property>
|
||||
|
@ -2316,7 +2298,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox19">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label44">
|
||||
<property name="visible">True</property>
|
||||
|
@ -2392,7 +2373,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox29">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment50">
|
||||
<property name="visible">True</property>
|
||||
|
@ -2417,7 +2397,7 @@ Disabled</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip" translatable="yes">If Deluge cannot find the database file at this location it will fallback to using DNS to resolve the peer's country.</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char">●</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
|
@ -2530,7 +2510,7 @@ Disabled</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow6">
|
||||
<widget class="GtkScrolledWindow" id="DaemonScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -2546,7 +2526,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox13">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label34">
|
||||
<property name="visible">True</property>
|
||||
|
@ -2588,7 +2567,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox14">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox12">
|
||||
<property name="visible">True</property>
|
||||
|
@ -2736,6 +2714,111 @@ Disabled</property>
|
|||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkFrame" id="AccountsFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment33">
|
||||
<property name="visible">True</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox22">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="accounts_listview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkVButtonBox" id="vbuttonbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<property name="layout_style">start</property>
|
||||
<child>
|
||||
<widget class="GtkButton" id="accounts_add">
|
||||
<property name="label">gtk-add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_accounts_add_clicked"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="accounts_edit">
|
||||
<property name="label">gtk-edit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_accounts_edit_clicked"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="accounts_delete">
|
||||
<property name="label">gtk-delete</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_accounts_delete_clicked"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="padding">4</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label61">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">10</property>
|
||||
<property name="ypad">10</property>
|
||||
<property name="label" translatable="yes"><b>Accounts</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="type">label_item</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
@ -2757,7 +2840,7 @@ Disabled</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow8">
|
||||
<widget class="GtkScrolledWindow" id="QueueScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -2773,7 +2856,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox9">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label46">
|
||||
<property name="visible">True</property>
|
||||
|
@ -2803,7 +2885,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="queue_prefs_box2">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<widget class="GtkFrame" id="frame10">
|
||||
|
@ -2821,7 +2902,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox6">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkCheckButton" id="chk_queue_new_top">
|
||||
<property name="label" translatable="yes">Queue new torrents to top</property>
|
||||
|
@ -2872,7 +2952,6 @@ Disabled</property>
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox20">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<widget class="GtkTable" id="table3">
|
||||
|
@ -3024,7 +3103,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox11">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<widget class="GtkTable" id="table2">
|
||||
|
@ -3234,7 +3312,7 @@ Disabled</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow9">
|
||||
<widget class="GtkScrolledWindow" id="ProxyScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -3250,7 +3328,6 @@ Disabled</property>
|
|||
<widget class="GtkVBox" id="vbox22">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label83">
|
||||
<property name="visible">True</property>
|
||||
|
@ -3278,7 +3355,6 @@ Disabled</property>
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox26">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<widget class="GtkFrame" id="frame23">
|
||||
|
@ -3372,7 +3448,7 @@ Disabled</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="adjustment">8080 0 65535 1 10 0</property>
|
||||
<property name="adjustment">100 0 65535 1 10 0</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
@ -3556,7 +3632,7 @@ HTTP W/ Auth</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="adjustment">8080 0 65535 1 10 0</property>
|
||||
<property name="adjustment">100 0 65535 1 10 0</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
@ -3740,7 +3816,7 @@ HTTP W/ Auth</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="adjustment">8080 0 65535 1 10 0</property>
|
||||
<property name="adjustment">100 0 65535 1 10 0</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
@ -3927,7 +4003,7 @@ HTTP W/ Auth</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="adjustment">8080 0 65535 1 10 0</property>
|
||||
<property name="adjustment">100 0 65535 1 10 0</property>
|
||||
<property name="numeric">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
@ -4042,7 +4118,7 @@ HTTP W/ Auth</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow13">
|
||||
<widget class="GtkScrolledWindow" id="CacheScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
|
@ -4055,7 +4131,6 @@ HTTP W/ Auth</property>
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox30">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label111">
|
||||
<property name="visible">True</property>
|
||||
|
@ -4087,7 +4162,6 @@ HTTP W/ Auth</property>
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox31">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkFrame" id="frame32">
|
||||
<property name="visible">True</property>
|
||||
|
@ -4132,9 +4206,9 @@ HTTP W/ Auth</property>
|
|||
<widget class="GtkSpinButton" id="spin_cache_size">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">512 0 99999 1 10 0</property>
|
||||
<property name="adjustment">100 0 99999 1 10 0</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="update_policy">if-valid</property>
|
||||
</widget>
|
||||
|
@ -4149,7 +4223,7 @@ HTTP W/ Auth</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="max_length">5</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="width_chars">5</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="adjustment">60 1 32000 1 10 0</property>
|
||||
|
@ -4197,7 +4271,6 @@ HTTP W/ Auth</property>
|
|||
<child>
|
||||
<widget class="GtkVBox" id="vbox32">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkFrame" id="frame34">
|
||||
<property name="visible">True</property>
|
||||
|
@ -4590,7 +4663,7 @@ HTTP W/ Auth</property>
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow5">
|
||||
<widget class="GtkScrolledWindow" id="PluginsScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
|
@ -4606,7 +4679,6 @@ HTTP W/ Auth</property>
|
|||
<widget class="GtkVBox" id="vbox28">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label51">
|
||||
<property name="visible">True</property>
|
||||
|
@ -4637,7 +4709,6 @@ HTTP W/ Auth</property>
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow12">
|
||||
<property name="visible">True</property>
|
||||
|
@ -4996,7 +5067,7 @@ HTTP W/ Auth</property>
|
|||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">9</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# menubar.py
|
||||
#
|
||||
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2011 Pedro Algarvio <ufs@ufsoft.org>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
|
@ -212,7 +213,9 @@ class MenuBar(component.Component):
|
|||
self.menuitem_change_owner.set_visible(False)
|
||||
|
||||
# Get Known accounts to allow chaning ownership
|
||||
client.core.get_known_accounts().addCallback(self._on_known_accounts)
|
||||
client.core.get_known_accounts().addCallback(
|
||||
self._on_known_accounts).addErrback(self._on_known_accounts_fail
|
||||
)
|
||||
|
||||
def stop(self):
|
||||
log.debug("MenuBar stopping")
|
||||
|
@ -354,9 +357,9 @@ class MenuBar(component.Component):
|
|||
|
||||
def show_move_storage_dialog(self, status):
|
||||
log.debug("show_move_storage_dialog")
|
||||
glade = gtk.glade.XML(
|
||||
pkg_resources.resource_filename("deluge.ui.gtkui",
|
||||
"glade/move_storage_dialog.glade"))
|
||||
glade = gtk.glade.XML(pkg_resources.resource_filename(
|
||||
"deluge.ui.gtkui", "glade/move_storage_dialog.glade"
|
||||
))
|
||||
dialog = glade.get_widget("move_storage_dialog")
|
||||
dialog.set_transient_for(self.window.window)
|
||||
entry = glade.get_widget("entry_destination")
|
||||
|
@ -439,10 +442,21 @@ class MenuBar(component.Component):
|
|||
}
|
||||
# widget: (header, type_str, image_stockid, image_filename, default)
|
||||
other_dialog_info = {
|
||||
"menuitem_down_speed": (_("Set Maximum Download Speed"), "KiB/s", None, "downloading.svg", -1.0),
|
||||
"menuitem_up_speed": (_("Set Maximum Upload Speed"), "KiB/s", None, "seeding.svg", -1.0),
|
||||
"menuitem_max_connections": (_("Set Maximum Connections"), "", gtk.STOCK_NETWORK, None, -1),
|
||||
"menuitem_upload_slots": (_("Set Maximum Upload Slots"), "", gtk.STOCK_SORT_ASCENDING, None, -1)
|
||||
"menuitem_down_speed": (
|
||||
_("Set Maximum Download Speed"),
|
||||
"KiB/s", None, "downloading.svg", -1.0
|
||||
),
|
||||
"menuitem_up_speed": (
|
||||
_("Set Maximum Upload Speed"),
|
||||
"KiB/s", None, "seeding.svg", -1.0
|
||||
),
|
||||
"menuitem_max_connections": (
|
||||
_("Set Maximum Connections"), "", gtk.STOCK_NETWORK, None, -1
|
||||
),
|
||||
"menuitem_upload_slots": (
|
||||
_("Set Maximum Upload Slots"),
|
||||
"", gtk.STOCK_SORT_ASCENDING, None, -1
|
||||
)
|
||||
}
|
||||
|
||||
# Show the other dialog
|
||||
|
@ -483,7 +497,15 @@ class MenuBar(component.Component):
|
|||
getattr(self.window.main_glade.get_widget(item), attr)()
|
||||
|
||||
def _on_known_accounts(self, known_accounts):
|
||||
log.debug("_on_known_accounts: %s", known_accounts)
|
||||
known_accounts_to_log = []
|
||||
for account in known_accounts:
|
||||
account_to_log = {}
|
||||
for key, value in account.copy().iteritems():
|
||||
if key == 'password':
|
||||
value = '*' * len(value)
|
||||
account_to_log[key] = value
|
||||
known_accounts_to_log.append(account_to_log)
|
||||
log.debug("_on_known_accounts: %s", known_accounts_to_log)
|
||||
if len(known_accounts) <= 1:
|
||||
return
|
||||
|
||||
|
@ -496,14 +518,18 @@ class MenuBar(component.Component):
|
|||
self.change_owner_submenu_items[None] = gtk.RadioMenuItem(maingroup)
|
||||
|
||||
for account in known_accounts:
|
||||
self.change_owner_submenu_items[account] = item = gtk.RadioMenuItem(maingroup, account)
|
||||
username = account["username"]
|
||||
item = gtk.RadioMenuItem(maingroup, username)
|
||||
self.change_owner_submenu_items[username] = item
|
||||
self.change_owner_submenu.append(item)
|
||||
item.connect("toggled", self._on_change_owner_toggled, account)
|
||||
item.connect("toggled", self._on_change_owner_toggled, username)
|
||||
|
||||
self.change_owner_submenu.show_all()
|
||||
self.change_owner_submenu_items[None].set_active(True)
|
||||
self.change_owner_submenu_items[None].hide()
|
||||
self.menuitem_change_owner.connect("activate", self._on_change_owner_submenu_active)
|
||||
self.menuitem_change_owner.connect(
|
||||
"activate", self._on_change_owner_submenu_active
|
||||
)
|
||||
self.menuitem_change_owner.set_submenu(self.change_owner_submenu)
|
||||
|
||||
def _on_known_accounts_fail(self, reason):
|
||||
|
@ -517,18 +543,18 @@ class MenuBar(component.Component):
|
|||
return
|
||||
|
||||
torrent_owner = component.get("TorrentView").get_torrent_status(selected[0])["owner"]
|
||||
for account, item in self.change_owner_submenu_items.iteritems():
|
||||
item.set_active(account == torrent_owner)
|
||||
for username, item in self.change_owner_submenu_items.iteritems():
|
||||
item.set_active(username == torrent_owner)
|
||||
|
||||
def _on_change_owner_toggled(self, widget, account):
|
||||
def _on_change_owner_toggled(self, widget, username):
|
||||
log.debug("_on_change_owner_toggled")
|
||||
update_torrents = []
|
||||
selected = component.get("TorrentView").get_selected_torrents()
|
||||
for torrent_id in selected:
|
||||
torrent_status = component.get("TorrentView").get_torrent_status(torrent_id)
|
||||
if torrent_status["owner"] != account:
|
||||
if torrent_status["owner"] != username:
|
||||
update_torrents.append(torrent_id)
|
||||
if update_torrents:
|
||||
log.debug("Setting torrent owner \"%s\" on %s", account, update_torrents)
|
||||
client.core.set_torrents_owner(update_torrents, account)
|
||||
log.debug("Setting torrent owner \"%s\" on %s", username, update_torrents)
|
||||
client.core.set_torrents_owner(update_torrents, username)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# preferences.py
|
||||
#
|
||||
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2011 Pedro Algarvio <ufs@ufsoft.org>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
|
@ -46,11 +47,14 @@ from deluge.ui.client import client
|
|||
import deluge.common
|
||||
import deluge.error
|
||||
import common
|
||||
import dialogs
|
||||
from deluge.configmanager import ConfigManager
|
||||
import deluge.configmanager
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
ACCOUNTS_USERNAME, ACCOUNTS_LEVEL, ACCOUNTS_PASSWORD = range(3)
|
||||
|
||||
class Preferences(component.Component):
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "Preferences")
|
||||
|
@ -81,6 +85,35 @@ class Preferences(component.Component):
|
|||
self.liststore.append([i, category])
|
||||
i += 1
|
||||
|
||||
# Setup accounts tab lisview
|
||||
self.accounts_levels_mapping = None
|
||||
self.accounts_authlevel = self.glade.get_widget("accounts_authlevel")
|
||||
self.accounts_liststore = gtk.ListStore(str, str, str, int)
|
||||
self.accounts_liststore.set_sort_column_id(ACCOUNTS_USERNAME,
|
||||
gtk.SORT_ASCENDING)
|
||||
self.accounts_listview = self.glade.get_widget("accounts_listview")
|
||||
self.accounts_listview.append_column(
|
||||
gtk.TreeViewColumn(
|
||||
_("Username"), gtk.CellRendererText(), text=ACCOUNTS_USERNAME
|
||||
)
|
||||
)
|
||||
self.accounts_listview.append_column(
|
||||
gtk.TreeViewColumn(
|
||||
_("Level"), gtk.CellRendererText(), text=ACCOUNTS_LEVEL
|
||||
)
|
||||
)
|
||||
password_column = gtk.TreeViewColumn(
|
||||
'password', gtk.CellRendererText(), text=ACCOUNTS_PASSWORD
|
||||
)
|
||||
self.accounts_listview.append_column(password_column)
|
||||
password_column.set_visible(False)
|
||||
self.accounts_listview.set_model(self.accounts_liststore)
|
||||
|
||||
self.accounts_listview.get_selection().connect(
|
||||
"changed", self._on_accounts_selection_changed
|
||||
)
|
||||
self.accounts_frame = self.glade.get_widget("AccountsFrame")
|
||||
|
||||
# Setup plugin tab listview
|
||||
self.plugin_liststore = gtk.ListStore(str, bool)
|
||||
self.plugin_liststore.set_sort_column_id(0, gtk.SORT_ASCENDING)
|
||||
|
@ -96,11 +129,13 @@ class Preferences(component.Component):
|
|||
|
||||
# Connect to the 'changed' event of TreeViewSelection to get selection
|
||||
# changes.
|
||||
self.treeview.get_selection().connect("changed",
|
||||
self.on_selection_changed)
|
||||
self.treeview.get_selection().connect(
|
||||
"changed", self.on_selection_changed
|
||||
)
|
||||
|
||||
self.plugin_listview.get_selection().connect("changed",
|
||||
self.on_plugin_selection_changed)
|
||||
self.plugin_listview.get_selection().connect(
|
||||
"changed", self.on_plugin_selection_changed
|
||||
)
|
||||
|
||||
self.glade.signal_autoconnect({
|
||||
"on_pref_dialog_delete_event": self.on_pref_dialog_delete_event,
|
||||
|
@ -114,7 +149,10 @@ class Preferences(component.Component):
|
|||
"on_button_find_plugins_clicked": self._on_button_find_plugins_clicked,
|
||||
"on_button_cache_refresh_clicked": self._on_button_cache_refresh_clicked,
|
||||
"on_combo_proxy_type_changed": self._on_combo_proxy_type_changed,
|
||||
"on_button_associate_magnet_clicked": self._on_button_associate_magnet_clicked
|
||||
"on_button_associate_magnet_clicked": self._on_button_associate_magnet_clicked,
|
||||
"on_accounts_add_clicked": self._on_accounts_add_clicked,
|
||||
"on_accounts_delete_clicked": self._on_accounts_delete_clicked,
|
||||
"on_accounts_edit_clicked": self._on_accounts_edit_clicked
|
||||
})
|
||||
|
||||
# These get updated by requests done to the core
|
||||
|
@ -191,9 +229,12 @@ class Preferences(component.Component):
|
|||
|
||||
component.get("PluginManager").run_on_show_prefs()
|
||||
|
||||
|
||||
# Update the preferences dialog to reflect current config settings
|
||||
self.core_config = {}
|
||||
if client.connected():
|
||||
self._get_accounts_tab_data()
|
||||
|
||||
def _on_get_config(config):
|
||||
self.core_config = config
|
||||
client.core.get_available_plugins().addCallback(_on_get_available_plugins)
|
||||
|
@ -832,6 +873,9 @@ class Preferences(component.Component):
|
|||
# Show the correct notebook page based on what row is selected.
|
||||
(model, row) = treeselection.get_selected()
|
||||
try:
|
||||
if model.get_value(row, 1) == _("Daemon"):
|
||||
# Let's see update the accounts related stuff
|
||||
self._get_accounts_tab_data()
|
||||
self.notebook.set_current_page(model.get_value(row, 0))
|
||||
except TypeError:
|
||||
pass
|
||||
|
@ -961,3 +1005,169 @@ class Preferences(component.Component):
|
|||
|
||||
def _on_button_associate_magnet_clicked(self, widget):
|
||||
common.associate_magnet_links(True)
|
||||
|
||||
|
||||
def _get_accounts_tab_data(self):
|
||||
def on_ok(accounts):
|
||||
self.accounts_frame.show()
|
||||
self._on_get_known_accounts(accounts)
|
||||
|
||||
def on_fail(failure):
|
||||
if failure.value.exception_type == 'NotAuthorizedError':
|
||||
self.accounts_frame.hide()
|
||||
else:
|
||||
dialogs.ErrorDialog(
|
||||
_("Server Side Error"),
|
||||
_("An error ocurred on the server"),
|
||||
self.pref_dialog, details=failure.value.logable()
|
||||
).run()
|
||||
client.core.get_known_accounts().addCallback(on_ok).addErrback(on_fail)
|
||||
|
||||
def _on_get_known_accounts(self, known_accounts):
|
||||
known_accounts_to_log = []
|
||||
for account in known_accounts:
|
||||
account_to_log = {}
|
||||
for key, value in account.copy().iteritems():
|
||||
if key == 'password':
|
||||
value = '*' * len(value)
|
||||
account_to_log[key] = value
|
||||
known_accounts_to_log.append(account_to_log)
|
||||
log.debug("_on_known_accounts: %s", known_accounts_to_log)
|
||||
|
||||
self.accounts_liststore.clear()
|
||||
|
||||
for account in known_accounts:
|
||||
iter = self.accounts_liststore.append()
|
||||
self.accounts_liststore.set_value(
|
||||
iter, ACCOUNTS_USERNAME, account['username']
|
||||
)
|
||||
self.accounts_liststore.set_value(
|
||||
iter, ACCOUNTS_LEVEL, account['authlevel']
|
||||
)
|
||||
self.accounts_liststore.set_value(
|
||||
iter, ACCOUNTS_PASSWORD, account['password']
|
||||
)
|
||||
|
||||
def _on_accounts_selection_changed(self, treeselection):
|
||||
log.debug("_on_accounts_selection_changed")
|
||||
(model, itr) = treeselection.get_selected()
|
||||
if not itr:
|
||||
return
|
||||
username = model[itr][0]
|
||||
if username:
|
||||
self.glade.get_widget("accounts_edit").set_sensitive(True)
|
||||
self.glade.get_widget("accounts_delete").set_sensitive(True)
|
||||
else:
|
||||
self.glade.get_widget("accounts_edit").set_sensitive(False)
|
||||
self.glade.get_widget("accounts_delete").set_sensitive(False)
|
||||
|
||||
def _on_accounts_add_clicked(self, widget):
|
||||
dialog = dialogs.AccountDialog(
|
||||
levels_mapping=client.auth_levels_mapping,
|
||||
parent=self.pref_dialog
|
||||
)
|
||||
|
||||
def dialog_finished(response_id):
|
||||
username = dialog.get_username()
|
||||
password = dialog.get_password()
|
||||
authlevel = dialog.get_authlevel()
|
||||
|
||||
def add_ok(rv):
|
||||
iter = self.accounts_liststore.append()
|
||||
self.accounts_liststore.set_value(
|
||||
iter, ACCOUNTS_USERNAME, username
|
||||
)
|
||||
self.accounts_liststore.set_value(
|
||||
iter, ACCOUNTS_LEVEL, authlevel
|
||||
)
|
||||
self.accounts_liststore.set_value(
|
||||
iter, ACCOUNTS_PASSWORD, password
|
||||
)
|
||||
|
||||
def add_fail(failure):
|
||||
if failure.value.exception_type == 'AuthManagerError':
|
||||
dialogs.ErrorDialog(
|
||||
_("Error Adding Account"),
|
||||
failure.value.exception_msg
|
||||
).run()
|
||||
else:
|
||||
dialogs.ErrorDialog(
|
||||
_("Error Adding Account"),
|
||||
_("An error ocurred while adding account"),
|
||||
self.pref_dialog, details=failure.value.logable()
|
||||
).run()
|
||||
|
||||
if response_id == gtk.RESPONSE_OK:
|
||||
client.core.create_account(
|
||||
username, password, authlevel
|
||||
).addCallback(add_ok).addErrback(add_fail)
|
||||
|
||||
dialog.run().addCallback(dialog_finished)
|
||||
|
||||
def _on_accounts_edit_clicked(self, widget):
|
||||
(model, itr) = self.accounts_listview.get_selection().get_selected()
|
||||
if not itr:
|
||||
return
|
||||
|
||||
dialog = dialogs.AccountDialog(
|
||||
model[itr][ACCOUNTS_USERNAME],
|
||||
model[itr][ACCOUNTS_PASSWORD],
|
||||
model[itr][ACCOUNTS_LEVEL],
|
||||
levels_mapping=client.auth_levels_mapping,
|
||||
parent=self.pref_dialog
|
||||
)
|
||||
|
||||
def dialog_finished(response_id):
|
||||
|
||||
def update_ok(rc):
|
||||
model.set_value(itr, ACCOUNTS_PASSWORD, dialog.get_username())
|
||||
model.set_value(itr, ACCOUNTS_LEVEL, dialog.get_authlevel())
|
||||
|
||||
def update_fail(failure):
|
||||
dialogs.ErrorDialog(
|
||||
_("Error Updating Account"),
|
||||
_("An error ocurred while updating account"),
|
||||
self.pref_dialog, details=failure.value.logable()
|
||||
).run()
|
||||
|
||||
if response_id == gtk.RESPONSE_OK:
|
||||
client.core.update_account(
|
||||
dialog.get_username(),
|
||||
dialog.get_password(),
|
||||
dialog.get_authlevel()
|
||||
).addCallback(update_ok).addErrback(update_fail)
|
||||
|
||||
dialog.run().addCallback(dialog_finished)
|
||||
|
||||
def _on_accounts_delete_clicked(self, widget):
|
||||
(model, itr) = self.accounts_listview.get_selection().get_selected()
|
||||
if not itr:
|
||||
return
|
||||
|
||||
username = model[itr][0]
|
||||
header = _("Remove Account")
|
||||
text = _("Are you sure you wan't do remove the account with the "
|
||||
"username \"%(username)s\"?" % dict(username=username))
|
||||
dialog = dialogs.YesNoDialog(header, text, parent=self.pref_dialog)
|
||||
|
||||
def dialog_finished(response_id):
|
||||
def remove_ok(rc):
|
||||
model.remove(itr)
|
||||
|
||||
def remove_fail(failure):
|
||||
if failure.value.exception_type == 'AuthManagerError':
|
||||
dialogs.ErrorDialog(
|
||||
_("Error Removing Account"),
|
||||
failure.value.exception_msg
|
||||
).run()
|
||||
else:
|
||||
dialogs.ErrorDialog(
|
||||
_("Error Removing Account"),
|
||||
_("An error ocurred while removing account"),
|
||||
self.pref_dialog, details=failure.value.logable()
|
||||
).run()
|
||||
if response_id == gtk.RESPONSE_YES:
|
||||
client.core.remove_account(
|
||||
username
|
||||
).addCallback(remove_ok).addErrback(remove_fail)
|
||||
dialog.run().addCallback(dialog_finished)
|
||||
|
|
Loading…
Reference in New Issue