diff --git a/CHANGELOG b/CHANGELOG
index 4cde9c2dfe..65fb7251b3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Added a connection manager à la gajim
* Fix warning in wizards
* Added possibility to use server-side icons
* Added additional gtk.Entry for filename on BinaryField
diff --git a/tryton/common/common.py b/tryton/common/common.py
index 64c62bdcff..9190b73c02 100644
--- a/tryton/common/common.py
+++ b/tryton/common/common.py
@@ -21,6 +21,7 @@ except ImportError:
import md5
import webbrowser
import traceback
+import threading
import tryton.rpc as rpc
import locale
import socket
@@ -35,7 +36,7 @@ try:
except ImportError:
ssl = None
import dis
-from threading import Lock
+from threading import Lock, Semaphore
_ = gettext.gettext
@@ -126,7 +127,11 @@ def find_in_path(name):
return val
return name
-def refresh_dblist(db_widget, host, port, dbtoload=None):
+def false_call(function, *args):
+ function(*args)
+ return None
+
+def refresh_dblist(host, port):
'''
Return the number of database available
or None if it is impossible to connect
@@ -137,22 +142,7 @@ def refresh_dblist(db_widget, host, port, dbtoload=None):
if hasattr(version, 'split'):
if version.split('.')[:2] != VERSION.split('.')[:2]:
return -1
- if not dbtoload:
- dbtoload = CONFIG['login.db']
- index = 0
- liststore = db_widget.get_model()
- liststore.clear()
- result = rpc.db_list(host, port)
- from tryton.gui.main import Main
- Main.get_main().refresh_ssl()
- if result is None:
- return None
- for db_num, dbname in enumerate(result):
- liststore.append([dbname])
- if dbname == dbtoload:
- index = db_num
- db_widget.set_active(index)
- return len(liststore)
+ return rpc.db_list(host, port)
def refresh_langlist(lang_widget, host, port):
liststore = lang_widget.get_model()
@@ -1030,6 +1020,65 @@ def generateColorscheme(masterColor, keys, light=0.06):
for i, key in enumerate(keys)])
+class DBProgress(object):
+
+ def __init__(self, host, port):
+ self.dbs, self.createdb = None, None
+ self.host, self.port = host, port
+ self.updated = threading.Event()
+ self.db_info = None
+
+ def start(self):
+ key = (self.host, self.port)
+ try:
+ dbs = refresh_dblist(self.host, self.port)
+ createdb = True
+ except Exception, exception:
+ if exception[0] == 'AccessDenied':
+ dbs, createdb = [], False
+ else:
+ raise
+ self.db_info = (dbs, createdb)
+ self.updated.set()
+
+ def update(self, combo, progressbar, dbname=''):
+ key = (self.host, self.port)
+ self.db_info = None
+ thread = threading.Thread(target=self.start).start()
+
+ i = 0
+ while not self.updated.isSet():
+ i += 1
+ time.sleep(0.1)
+ if i >= 10:
+ progressbar.show()
+ progressbar.pulse()
+ while gtk.events_pending():
+ gtk.main_iteration()
+ progressbar.hide()
+ dbs, createdb = self.db_info
+
+ if dbs is None:
+ return None, False
+ elif dbs == -1:
+ return -1, False
+
+ from tryton.gui.main import Main
+ Main.get_main().refresh_ssl()
+ liststore = combo.get_model()
+ liststore.clear()
+ index = -1
+ for db_num, db_name in enumerate(dbs):
+ liststore.append([db_name])
+ if db_name == dbname:
+ index = db_num
+ if index == -1:
+ index = 0
+ combo.set_active(index)
+
+ return len(dbs), createdb
+
+
class RPCProgress(object):
def __init__(self, method, args, parent):
diff --git a/tryton/gui/main.py b/tryton/gui/main.py
index 1816897afa..5999c8a83d 100644
--- a/tryton/gui/main.py
+++ b/tryton/gui/main.py
@@ -1145,13 +1145,13 @@ class Main(object):
raise
return ([], [])
- def sig_login(self, widget=None, dbname=False, res=None):
+ def sig_login(self, widget=None, profile_name=False, res=None):
if not self.sig_logout(widget, disconnect=False):
return
if not res:
try:
dblogin = DBLogin(self.window)
- res = dblogin.run(dbname, self.window)
+ res = dblogin.run(profile_name, self.window)
except Exception, exception:
if exception.args == ('QueryCanceled',):
return False
@@ -1513,7 +1513,7 @@ class Main(object):
def sig_db_new(self, widget):
if not self.sig_logout(widget):
return False
- dia = DBCreate(self.sig_login)
+ dia = DBCreate(sig_login=self.sig_login)
res = dia.run(self.window)
if res:
CONFIG.save()
diff --git a/tryton/gui/window/dbcreate.py b/tryton/gui/window/dbcreate.py
index 11a7179e8c..ca69c12d23 100644
--- a/tryton/gui/window/dbcreate.py
+++ b/tryton/gui/window/dbcreate.py
@@ -1,13 +1,14 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from __future__ import with_statement
-
+import ConfigParser
+import os
import gtk
import gobject
import gettext
import re
import tryton.common as common
-from tryton.config import CONFIG, TRYTON_ICON, PIXMAPS_DIR
+from tryton.config import CONFIG, TRYTON_ICON, PIXMAPS_DIR, get_config_dir
import tryton.rpc as rpc
_ = gettext.gettext
@@ -72,8 +73,6 @@ class DBCreate(object):
except Exception:
self.server_connection_state(False)
return False
- CONFIG['login.server'] = host
- CONFIG['login.port'] = port
return True
def event_passwd_clear(self, widget, event, data=None):
@@ -131,10 +130,11 @@ class DBCreate(object):
gobject.idle_add(_move_cursor)
entry.stop_emission("insert-text")
- def __init__(self, sig_login=None):
- """
- This method defines the complete GUI.
- """
+ def __init__(self, host=None, port=None, sig_login=None):
+ self.host = host
+ self.port = port
+
+ # GTK Stuffs
self.dialog = gtk.Dialog(
title= _("Create new database"),
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
@@ -178,7 +178,6 @@ class DBCreate(object):
self.entry_server_connection.set_sensitive(False)
self.entry_server_connection.unset_flags(gtk.CAN_FOCUS)
self.entry_server_connection.set_editable(False)
- table.attach(self.entry_server_connection, 1, 2, 1, 2)
self.tooltips.set_tip(self.entry_server_connection, _("This is the URL of " \
"the server. Use server 'localhost' and port '8070' if " \
"the server is installed on this computer. Click on 'Change' to " \
@@ -192,6 +191,10 @@ class DBCreate(object):
table.attach(self.button_server_change, 2, 3, 1, 2, yoptions=False, xoptions=gtk.FILL)
self.tooltips.set_tip(self.button_server_change, _("Setup the " \
"server connection..."))
+ self.button_server_change.set_sensitive(not bool(self.host
+ and self.port))
+
+ table.attach(self.entry_server_connection, 1, 2, 1, 2)
self.label_serverpasswd = gtk.Label(_("Tryton Server Password:"))
self.label_serverpasswd.set_justify(gtk.JUSTIFY_RIGHT)
self.label_serverpasswd.set_alignment(1, 0.5)
@@ -293,30 +296,22 @@ class DBCreate(object):
self.dialog.set_transient_for(parent)
self.dialog.show_all()
- if not CONFIG['login.host']:
- self.label_server_setup.hide()
- self.label_server.hide()
- self.entry_server_connection.hide()
- self.button_server_change.hide()
- self.label_serverpasswd.hide()
- self.entry_serverpasswd.hide()
- self.hseparator.hide()
-
pass_widget = self.entry_serverpasswd
change_button = self.button_server_change
admin_passwd = self.entry_adminpasswd
admin_passwd2 = self.entry_adminpasswd2
-
change_button.connect_after('clicked', self.server_change, self.dialog)
- host = CONFIG['login.server']
- port = int(CONFIG['login.port'])
- url = '%s:%d' % (host, port)
+
+ if self.host and self.port:
+ url = '%s:%d' % (self.host, self.port)
+ else:
+ url = ''
self.entry_server_connection.set_text(url)
liststore = gtk.ListStore(str, str)
self.combo_language.set_model(liststore)
try:
- common.refresh_langlist(self.combo_language, host, port)
+ common.refresh_langlist(self.combo_language, self.host, self.port)
except Exception:
self.button_create.set_sensitive(False)
@@ -372,9 +367,8 @@ class DBCreate(object):
self.entry_dbname.grab_focus()
continue
else: # Everything runs fine, break the block here
- CONFIG['login.server'] = host = url_m.group(1)
- CONFIG['login.port'] = port = url_m.group(2)
- CONFIG["login.login"] = "admin"
+ host = url_m.group(1)
+ port = url_m.group(2)
try:
rpcprogress = common.RPCProgress('db_exec',
(host, int(port), 'create', dbname, passwd,
@@ -409,10 +403,27 @@ class DBCreate(object):
parent.present()
self.dialog.destroy()
if self.sig_login:
- self.sig_login(dbname=dbname)
+ profile_cfg = os.path.join(get_config_dir(),
+ 'profiles.cfg')
+ profiles = ConfigParser.SafeConfigParser()
+ if os.path.exists(profile_cfg):
+ profiles.read(profile_cfg)
+ i, profile_name = 0, dbname
+ while profile_name in profiles.sections():
+ i += 1
+ profile_name = '%s_%s' % (dbname, i)
+ profiles.add_section(profile_name)
+ profiles.set(profile_name, 'host', host)
+ profiles.set(profile_name, 'port', port)
+ profiles.set(profile_name, 'database', dbname)
+ profiles.set(profile_name, 'username', '')
+ with open(profile_cfg, 'wb') as configfile:
+ profiles.write(configfile)
+ self.sig_login(profile_name=profile_name)
break
break
parent.present()
self.dialog.destroy()
+ return dbname
diff --git a/tryton/gui/window/dbdumpdrop.py b/tryton/gui/window/dbdumpdrop.py
index e904edfbab..6749e29015 100644
--- a/tryton/gui/window/dbdumpdrop.py
+++ b/tryton/gui/window/dbdumpdrop.py
@@ -1,5 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of this
# repository contains the full copyright notices and license terms.
+import threading
+
import gtk
import gettext
import tryton.common as common
@@ -13,19 +15,22 @@ class DBBackupDrop(object):
Widget for database backup and drop.
"""
@staticmethod
- def refreshlist(widget, db_widget, label, host, port):
- res = common.refresh_dblist(db_widget, host, port)
- if res is None or res == -1:
- if res is None:
+ def refreshlist(widget, db_widget, label, db_progress, host, port):
+ db_widget.hide()
+ label.hide()
+ dbprogress = common.DBProgress(host, port)
+ dbs, createdb = dbprogress.update(db_widget, db_progress)
+ if dbs is None or dbs == -1:
+ if dbs is None:
label.set_label('' + \
_('Could not connect to server!') + '')
else:
label.set_label('' + \
- _('This client version is not compatible with the server!') +\
- '')
+ _('This client version is not compatible with the server!')
+ + '')
db_widget.hide()
label.show()
- elif res == 0:
+ elif dbs == 0:
label.set_label('' + \
_('No database found, you must create one!') + '')
db_widget.hide()
@@ -33,17 +38,17 @@ class DBBackupDrop(object):
else:
label.hide()
db_widget.show()
- return res
+ return dbs
@staticmethod
- def refreshlist_ask(widget, server_widget, db_widget, label, parent=None):
+ def refreshlist_ask(widget, server_widget, db_widget, label, db_progress,
+ parent=None):
res = common.request_server(server_widget, parent)
if not res:
return None
host, port = res
- if DBBackupDrop.refreshlist(widget, db_widget, label, host, port):
- CONFIG['login.server'] = host
- CONFIG['login.port'] = port
+ DBBackupDrop.refreshlist(widget, db_widget, label, db_progress, host,
+ port)
return (host, port)
def event_show_button_ok(self, widget, event, data=None):
@@ -157,11 +162,18 @@ class DBBackupDrop(object):
vbox_combo = gtk.VBox()
self.combo_database = gtk.ComboBox()
+ self.db_progressbar = gtk.ProgressBar()
self.combo_database_label = gtk.Label()
self.combo_database_label.set_use_markup(True)
self.combo_database_label.set_alignment(0, 1)
- vbox_combo.pack_start(self.combo_database, True, True, 0)
- vbox_combo.pack_start(self.combo_database_label, False, False, 0)
+ vbox_combo.pack_start(self.combo_database, True, True)
+ vbox_combo.pack_start(self.combo_database_label, False, False)
+ vbox_combo.pack_start(self.db_progressbar, True, True)
+ width, height = 0, 0
+ for child in vbox_combo.get_children():
+ cwidth, cheight = child.size_request()
+ width, height = max(width, cwidth), max(height, cheight)
+ vbox_combo.set_size_request(width, height)
table.attach(vbox_combo, 1, 3, 3, 4, yoptions=gtk.FILL)
self.label_serverpasswd = gtk.Label(_("Tryton Server Password:"))
@@ -188,33 +200,24 @@ class DBBackupDrop(object):
self.dialog.set_transient_for(parent)
self.dialog.show_all()
- if not CONFIG['login.host']:
- self.label_server.hide()
- self.entry_server_connection.hide()
- self.button_server_change.hide()
- self.label_serverpasswd.hide()
- self.entry_serverpasswd.hide()
-
pass_widget = self.entry_serverpasswd
server_widget = self.entry_server_connection
db_widget = self.combo_database
+ db_progress = self.db_progressbar
label = self.combo_database_label
- host = CONFIG['login.server']
- port = int(CONFIG['login.port'])
- url = '%s:%d' % (host, port)
- server_widget.set_text(url)
+ server_widget.set_text('')
liststore = gtk.ListStore(str)
db_widget.set_model(liststore)
cell = gtk.CellRendererText()
db_widget.pack_start(cell, True)
db_widget.add_attribute(cell, 'text', 0)
- res = self.refreshlist(None, db_widget, label, host, port)
+ res = self.refreshlist(None, db_widget, label, db_progress, '', '')
change_button = self.button_server_change
change_button.connect_after('clicked', DBBackupDrop.refreshlist_ask, \
- server_widget, db_widget, label, self.dialog)
+ server_widget, db_widget, label, db_progress, self.dialog)
while True:
database = False
diff --git a/tryton/gui/window/dblogin.py b/tryton/gui/window/dblogin.py
index 1816323871..65abe999f4 100644
--- a/tryton/gui/window/dblogin.py
+++ b/tryton/gui/window/dblogin.py
@@ -1,19 +1,351 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
+from __future__ import with_statement
+
+import ConfigParser
import gtk
+import gobject
import os
import re
import gettext
+import threading
+import time
+
+from tryton.version import VERSION
import tryton.common as common
-from tryton.config import CONFIG, TRYTON_ICON, PIXMAPS_DIR
+from tryton.config import CONFIG, TRYTON_ICON, PIXMAPS_DIR, get_config_dir
import tryton.rpc as rpc
from tryton.gui.window.dbcreate import DBCreate
_ = gettext.gettext
+class DBListEditor(object):
+
+ def __init__(self, parent, profile_store, profiles):
+ self.profiles = profiles
+ self.current_database = None
+ self.old_profile, self.current_profile = None, None
+ self.updating_db = False
+
+ # GTK Stuffs
+ self.parent = parent
+ self.dialog = gtk.Dialog(title=_(u'Profile Editor'), parent=parent,
+ flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
+ self.cancel_button = self.dialog.add_button(gtk.STOCK_CANCEL,
+ gtk.RESPONSE_CANCEL)
+ self.ok_button = self.dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
+ self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
+ self.dialog.set_has_separator(True)
+ self.dialog.set_icon(TRYTON_ICON)
+
+ hpaned = gtk.HPaned()
+ vbox_profiles = gtk.VBox(homogeneous=False, spacing=6)
+ self.cell = gtk.CellRendererText()
+ self.cell.set_property('editable', True)
+ self.cell.connect('edited', self.edit_profilename)
+ self.cell.connect('editing-started', self.edit_started)
+ self.cell.connect('editing-canceled', self.edit_canceled)
+ self.profile_tree = gtk.TreeView()
+ self.profile_tree.set_model(profile_store)
+ self.profile_tree.insert_column_with_attributes(-1, _(u'Profile'),
+ self.cell, text=0)
+ self.profile_tree.connect('cursor-changed', self.profile_selected)
+ scroll = gtk.ScrolledWindow()
+ scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scroll.add(self.profile_tree)
+ self.add_button = gtk.Button(_(u'_Add'))
+ self.add_button.connect('clicked', self.profile_create)
+ add_image = gtk.Image()
+ add_image.set_from_stock('gtk-add', gtk.ICON_SIZE_BUTTON)
+ self.add_button.set_image(add_image)
+ self.remove_button = gtk.Button(_(u'_Remove'))
+ self.remove_button.connect('clicked', self.profile_delete)
+ remove_image = gtk.Image()
+ remove_image.set_from_stock('gtk-remove', gtk.ICON_SIZE_BUTTON)
+ self.remove_button.set_image(remove_image)
+ vbox_profiles.pack_start(scroll, expand=True, fill=True)
+ vbox_profiles.pack_start(self.add_button, expand=False, fill=True)
+ vbox_profiles.pack_start(self.remove_button, expand=False, fill=True)
+ hpaned.add1(vbox_profiles)
+
+ table = gtk.Table(4, 2, homogeneous=False)
+ table.set_row_spacings(3)
+ table.set_col_spacings(3)
+ host = gtk.Label(_(u'Hostname:'))
+ host.set_alignment(1, 0.5)
+ host.set_padding(3, 3)
+ self.host_entry = gtk.Entry()
+ self.host_entry.connect('focus-out-event', self.display_dbwidget)
+ self.host_entry.connect('changed', self.update_profiles, 'host')
+ self.host_entry.set_activates_default(True)
+ table.attach(host, 0, 1, 1, 2, yoptions=False, xoptions=gtk.FILL)
+ table.attach(self.host_entry, 1, 2, 1, 2, yoptions=False)
+ port = gtk.Label(_(u'Port:'))
+ port.set_alignment(1, 0.5)
+ port.set_padding(3, 3)
+ self.port_entry = gtk.Entry()
+ self.port_entry.connect('focus-out-event', self.display_dbwidget)
+ self.port_entry.connect('changed', self.update_profiles, 'port')
+ self.port_entry.set_activates_default(True)
+ table.attach(port, 0, 1, 2, 3, yoptions=False, xoptions=gtk.FILL)
+ table.attach(self.port_entry, 1, 2, 2, 3, yoptions=False)
+ database = gtk.Label(_(u'Database:'))
+ database.set_alignment(1, 0.5)
+ database.set_padding(3, 3)
+ self.database_entry = gtk.Entry()
+ self.database_entry.connect('changed', self.dbentry_changed)
+ self.database_entry.connect('changed', self.update_profiles, 'database')
+ self.database_entry.set_activates_default(True)
+ self.database_label = gtk.Label()
+ self.database_label.set_use_markup(True)
+ self.database_label.set_alignment(0, 0.5)
+ self.database_combo = gtk.ComboBox()
+ dbstore = gtk.ListStore(gobject.TYPE_STRING)
+ cell = gtk.CellRendererText()
+ self.database_combo.pack_start(cell, True)
+ self.database_combo.add_attribute(cell, 'text', 0)
+ self.database_combo.set_model(dbstore)
+ self.database_combo.connect('changed', self.dbcombo_changed)
+ self.database_button = gtk.Button(_(u'Create'))
+ self.database_button.connect('clicked', self.db_create)
+ self.database_progressbar= gtk.ProgressBar()
+ self.database_progressbar.set_text(_(u'Fetching databases list'))
+ image = gtk.Image()
+ image.set_from_stock('tryton-new', gtk.ICON_SIZE_BUTTON)
+ self.database_button.set_image(image)
+ db_box = gtk.VBox(homogeneous=True)
+ db_box.pack_start(self.database_entry)
+ db_box.pack_start(self.database_combo)
+ db_box.pack_start(self.database_label)
+ db_box.pack_start(self.database_button)
+ db_box.pack_start(self.database_progressbar)
+ # Compute size_request of box in order to prevent "form jumping"
+ width, height = 0, 0
+ for child in db_box.get_children():
+ cwidth, cheight = child.size_request()
+ width, height = max(width, cwidth), max(height, cheight)
+ db_box.set_size_request(width, height)
+ table.attach(database, 0, 1, 3, 4, yoptions=False, xoptions=gtk.FILL)
+ table.attach(db_box, 1, 2, 3, 4, yoptions=False)
+ username = gtk.Label(_(u'Username:'))
+ username.set_alignment(1, 0.5)
+ username.set_padding(3, 3)
+ self.username_entry = gtk.Entry()
+ self.username_entry.connect('changed', self.update_profiles, 'username')
+ self.username_entry.set_activates_default(True)
+ table.attach(username, 0, 1, 4, 5, yoptions=False, xoptions=gtk.FILL)
+ table.attach(self.username_entry, 1, 2, 4, 5, yoptions=False)
+ hpaned.add2(table)
+ hpaned.set_position(250)
+
+ self.dialog.vbox.pack_start(hpaned)
+ self.dialog.set_default_size(640, 350)
+ self.dialog.set_default_response(gtk.RESPONSE_ACCEPT)
+
+ def run(self):
+ self.dialog.show_all()
+ self.clear_entries()
+ model = self.profile_tree.get_model()
+ if model:
+ self.profile_tree.get_selection().select_path((0,))
+ self.profile_selected(self.profile_tree)
+ self.dialog.run()
+ self.parent.present()
+ self.dialog.destroy()
+
+ def _current_profile(self):
+ model, selection = self.profile_tree.get_selection().get_selected()
+ if not selection:
+ return {'name': None, 'iter': None}
+ return {'name': model[selection][0], 'iter': selection}
+
+ def clear_entries(self):
+ for entryname in ('host', 'port', 'database', 'username'):
+ entry = getattr(self, '%s_entry' % entryname)
+ if entryname == 'port':
+ entry.set_text('8070')
+ else:
+ entry.set_text('')
+ self.current_database = None
+ self.database_combo.set_active(-1)
+ self.database_combo.get_model().clear()
+ self.hide_database_info()
+
+ def hide_database_info(self):
+ self.database_entry.hide()
+ self.database_combo.hide()
+ self.database_label.hide()
+ self.database_button.hide()
+ self.database_progressbar.hide()
+
+ def profile_create(self, button):
+ self.clear_entries()
+ model = self.profile_tree.get_model()
+ selection = self.profile_tree.get_selection()
+ model.append(['', False])
+ column = self.profile_tree.get_column(0)
+ self.profile_tree.set_cursor(len(model)-1, column, start_editing=True)
+
+ def profile_delete(self, button):
+ self.clear_entries()
+ model, selection = self.profile_tree.get_selection().get_selected()
+ if not selection:
+ return
+ profile_name = model[selection][0]
+ self.profiles.remove_section(profile_name)
+ del model[selection]
+
+ def profile_selected(self, treeview):
+ self.old_profile = self.current_profile
+ self.current_profile = self._current_profile()
+ if not self.current_profile['name']:
+ return
+ if self.updating_db:
+ self.current_profile = self.old_profile
+ selection = treeview.get_selection()
+ selection.select_iter(self.old_profile['iter'])
+ return
+ self.clear_entries()
+ fields = ('host', 'port', 'database', 'username')
+ for field in fields:
+ entry = getattr(self, '%s_entry' % field)
+ try:
+ entry_value = self.profiles.get(self.current_profile['name'],
+ field)
+ entry.set_text(entry_value)
+ if field == 'database':
+ self.current_database = entry_value
+ except ConfigParser.NoOptionError:
+ pass
+
+ self.display_dbwidget(None, None, self.current_database)
+
+ def edit_canceled(self, renderer):
+ model = self.profile_tree.get_model()
+ for i, row in enumerate(list(model)):
+ if not row[0]:
+ del model[i]
+
+ def check_edit_cancel(self, editable, event, renderer, path):
+ renderer.emit('edited', path, editable.get_text())
+
+ def edit_started(self, renderer, editable, path):
+ if isinstance(editable, gtk.Entry):
+ editable.connect('focus-out-event', self.check_edit_cancel,
+ renderer, path)
+
+ def edit_profilename(self, renderer, path, newtext):
+ model = self.profile_tree.get_model()
+ oldname = model[path][0]
+ if oldname == newtext == '':
+ del model[path]
+ return
+ elif oldname == newtext or newtext == '':
+ return
+ if newtext in self.profiles.sections():
+ del model[path]
+ return
+ elif oldname in self.profiles.sections():
+ self.profiles.add_section(newtext)
+ for itemname, value in self.profiles.items(oldname):
+ self.profiles.set(newtext, itemname, value)
+ self.profiles.remove_section(oldname)
+ model[path][0] = newtext
+ else:
+ model[path][0] = newtext
+ self.profiles.add_section(newtext)
+ self.current_profile = self._current_profile()
+ self.host_entry.grab_focus()
+
+ def update_profiles(self, editable, entryname):
+ new_value = editable.get_text()
+ if not new_value:
+ return
+ section = self._current_profile()['name']
+ self.profiles.set(section, entryname, new_value)
+ self.validate_profile(section)
+
+ def validate_profile(self, profile_name):
+ model, selection = self.profile_tree.get_selection().get_selected()
+ if not selection:
+ return
+ active = all(self.profiles.has_option(profile_name, option)
+ for option in ('host', 'port', 'database'))
+ model[selection][1] = active
+
+ def display_dbwidget(self, entry, event, dbname=None):
+ host = self.host_entry.get_text()
+ port = self.port_entry.get_text()
+ if not (host and port):
+ return
+ if dbname is None:
+ dbname = self.current_database
+
+ dbprogress = common.DBProgress(host, int(port))
+ self.hide_database_info()
+ self.add_button.set_sensitive(False)
+ self.remove_button.set_sensitive(False)
+ self.ok_button.set_sensitive(False)
+ self.cancel_button.set_sensitive(False)
+ self.cell.set_property('editable', False)
+ self.updating_db = True
+ dbs, createdb = dbprogress.update(self.database_combo,
+ self.database_progressbar, dbname)
+ self.updating_db = False
+
+ if dbs is None and createdb is None:
+ pass
+ elif dbs is None or dbs == -1:
+ if dbs is None:
+ label = _(u'Could not connect to the server')
+ else:
+ label = _(u'Incompatible version of the server')
+ self.database_label.set_label('%s' % label)
+ self.database_label.show()
+ elif dbs == 0:
+ if createdb:
+ self.database_button.show()
+ else:
+ self.database_entry.show()
+ else:
+ self.database_entry.set_text(dbname if dbname else '')
+ self.database_combo.show()
+
+ self.add_button.set_sensitive(True)
+ self.remove_button.set_sensitive(True)
+ self.ok_button.set_sensitive(True)
+ self.cancel_button.set_sensitive(True)
+ self.cell.set_property('editable', True)
+
+ def db_create(self, button):
+ if not self.current_profile['name']:
+ return
+ host = self.host_entry.get_text()
+ port = int(self.port_entry.get_text())
+ dia = DBCreate(host, port)
+ dbname = dia.run(self.dialog)
+ self.username_entry.set_text('admin')
+ self.display_dbwidget(None, None, dbname)
+
+ def dbcombo_changed(self, combobox):
+ dbname = combobox.get_active_text()
+ if dbname:
+ self.current_database = dbname
+ self.profiles.set(self.current_profile['name'], 'database', dbname)
+ self.validate_profile(self.current_profile['name'])
+
+ def dbentry_changed(self, entry):
+ dbname = entry.get_text()
+ if dbname:
+ self.current_database = dbname
+ self.profiles.set(self.current_profile['name'], 'database', dbname)
+ self.validate_profile(self.current_profile['name'])
+
+
class DBLogin(object):
def __init__(self, parent):
+ # GTK Stuffs
self.dialog = gtk.Dialog(title=_('Login'), parent=parent,
flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
@@ -25,7 +357,8 @@ class DBLogin(object):
img_cancel = gtk.Image()
img_cancel.set_from_stock('tryton-cancel', gtk.ICON_SIZE_BUTTON)
button_cancel.set_image(img_cancel)
- tooltips.set_tip(button_cancel, _('Cancel connection to the Tryton server'))
+ tooltips.set_tip(button_cancel,
+ _('Cancel connection to the Tryton server'))
self.dialog.add_action_widget(button_cancel, gtk.RESPONSE_CANCEL)
self.button_connect = gtk.Button(_('C_onnect'))
img_connect = gtk.Image()
@@ -42,179 +375,126 @@ class DBLogin(object):
ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#1b2019"))
ebox.add(image)
self.dialog.vbox.pack_start(ebox)
- table_main = gtk.Table(4, 3, False)
- table_main.set_border_width(10)
- table_main.set_row_spacings(3)
- table_main.set_col_spacings(3)
- self.dialog.vbox.pack_start(table_main, True, True, 0)
- vbox_combo = gtk.VBox()
- self.hbox_combo = gtk.HBox()
- self.combo_database = gtk.ComboBox()
- self.combo_label = gtk.Label()
- self.combo_label.set_use_markup(True)
- self.combo_label.set_alignment(0, 0.5)
- self.combo_button = gtk.Button(_('C_reate'))
- self.combo_button.connect('clicked', self.db_create)
+ self.table_main = gtk.Table(3, 3, False)
+ self.table_main.set_border_width(10)
+ self.table_main.set_row_spacings(3)
+ self.table_main.set_col_spacings(3)
+ self.dialog.vbox.pack_start(self.table_main, True, True, 0)
+ self.profile_store = gtk.ListStore(gobject.TYPE_STRING,
+ gobject.TYPE_BOOLEAN)
+ self.combo_profile = gtk.ComboBox()
+ cell = gtk.CellRendererText()
+ self.combo_profile.pack_start(cell, True)
+ self.combo_profile.add_attribute(cell, 'text', 0)
+ self.combo_profile.add_attribute(cell, 'sensitive', 1)
+ self.combo_profile.set_model(self.profile_store)
+ self.combo_profile.connect('changed', self.profile_changed)
+ self.profile_label = gtk.Label(_(u'Profile:'))
+ self.profile_label.set_justify(gtk.JUSTIFY_RIGHT)
+ self.profile_label.set_alignment(1, 0.5)
+ self.profile_label.set_padding(3, 3)
+ self.profile_button = gtk.Button(_('_Manage profiles'))
+ self.profile_button.connect('clicked', self.profile_manage)
+ self.table_main.attach(self.profile_label, 0, 1, 0, 1,
+ xoptions=gtk.FILL)
+ self.table_main.attach(self.combo_profile, 1, 2, 0, 1)
+ self.table_main.attach(self.profile_button, 2, 3, 0, 1,
+ xoptions=gtk.FILL)
image = gtk.Image()
- image.set_from_stock('tryton-new', gtk.ICON_SIZE_BUTTON)
- self.combo_button.set_image(image)
- vbox_combo.pack_start(self.combo_database, True, True, 0)
- self.hbox_combo.pack_start(self.combo_label, True, True, 0)
- self.hbox_combo.pack_start(self.combo_button, False, False, 0)
- vbox_combo.pack_start(self.hbox_combo, True, True, 0)
- table_main.attach(vbox_combo, 1, 3, 1, 2)
+ image.set_from_stock('gtk-edit', gtk.ICON_SIZE_BUTTON)
+ self.profile_button.set_image(image)
self.entry_password = gtk.Entry()
self.entry_password.set_visibility(False)
self.entry_password.set_activates_default(True)
- table_main.attach(self.entry_password, 1, 3, 3, 4)
+ self.table_main.attach(self.entry_password, 1, 3, 3, 4)
self.entry_login = gtk.Entry()
self.entry_login.set_activates_default(True)
- table_main.attach(self.entry_login, 1, 3, 2, 3)
- self.label_server = gtk.Label()
- self.label_server.set_text(_("Server:"))
- self.label_server.set_alignment(1, 0.5)
- self.label_server.set_padding(3, 3)
- table_main.attach(self.label_server, 0, 1, 0, 1, xoptions=gtk.FILL)
- label_database = gtk.Label()
- label_database.set_text(_("Database:"))
- label_database.set_alignment(1, 0.5)
- label_database.set_padding(3, 3)
- table_main.attach(label_database, 0, 1, 1, 2, xoptions=gtk.FILL)
- self.entry_server = gtk.Entry()
- table_main.attach(self.entry_server, 1, 2, 0, 1)
- self.entry_server.set_sensitive(False)
- self.entry_server.unset_flags(gtk.CAN_FOCUS)
- self.entry_server.set_editable(False)
- self.entry_server.set_text("localhost")
- self.entry_server.set_activates_default(True)
- self.entry_server.set_width_chars(16)
- self.button_server = gtk.Button(label=_("C_hange"), stock=None,
- use_underline=True)
- tooltips.set_tip(self.button_server,
- _('Configure the Tryton server connection'))
- table_main.attach(self.button_server, 2, 3, 0, 1, xoptions=gtk.FILL)
+ self.table_main.attach(self.entry_login, 1, 3, 2, 3)
label_password = gtk.Label(str = _("Password:"))
label_password.set_justify(gtk.JUSTIFY_RIGHT)
label_password.set_alignment(1, 0.5)
label_password.set_padding(3, 3)
- table_main.attach(label_password, 0, 1, 3, 4, xoptions=gtk.FILL)
+ self.table_main.attach(label_password, 0, 1, 3, 4, xoptions=gtk.FILL)
label_username = gtk.Label(str = _("User name:"))
label_username.set_alignment(1, 0.5)
label_username.set_padding(3, 3)
- table_main.attach(label_username, 0, 1, 2, 3, xoptions=gtk.FILL)
+ self.table_main.attach(label_username, 0, 1, 2, 3, xoptions=gtk.FILL)
self.entry_password.grab_focus()
- @staticmethod
- def refreshlist(widget, db_widget, label, button, host, port,
- butconnect=None):
- res = common.refresh_dblist(db_widget, host, port)
- if res is None or res == -1:
- if res is None:
- label.set_label('' + _('Could not connect to server!') + \
- '')
- else:
- label.set_label('' + \
- _('Incompatible version of the server!') + '')
- db_widget.hide()
- label.show()
- button.hide()
- if butconnect:
- butconnect.set_sensitive(False)
- elif res == 0:
- label.set_label('' + \
- _('No database found, you must create one!') + '')
- db_widget.hide()
- label.show()
- button.show()
- if butconnect:
- butconnect.set_sensitive(False)
+ # Profile informations
+ self.profile_cfg = os.path.join(get_config_dir(), 'profiles.cfg')
+ self.profiles = ConfigParser.SafeConfigParser({'port': '8070'})
+ if not os.path.exists(self.profile_cfg):
+ short_version = '.'.join(VERSION.split('.', 2)[:2])
+ name = 'demo%s.tryton.org' % short_version
+ self.profiles.add_section(name)
+ self.profiles.set(name, 'host', name)
+ self.profiles.set(name, 'port', '8070')
+ self.profiles.set(name, 'database', 'demo%s' % short_version)
+ self.profiles.set(name, 'username', 'demo')
else:
- label.hide()
- button.hide()
- db_widget.show()
- if butconnect:
- butconnect.set_sensitive(True)
- return res
+ self.profiles.read(self.profile_cfg)
+ for section in self.profiles.sections():
+ active = all(self.profiles.has_option(section, option)
+ for option in ('host', 'port', 'database'))
+ self.profile_store.append([section, active])
- @staticmethod
- def refreshlist_ask(widget, server_widget, db_widget, label, button,
- butconnect=False, host=False, port=0, parent=None):
- res = common.request_server(server_widget, parent) or (host, port)
- if not res:
- return False
- host, port = res
- res = DBLogin.refreshlist(widget, db_widget, label, button, host, port,
- butconnect)
- if res:
- CONFIG['login.server'] = host
- CONFIG['login.port'] = port
- return res
+ def profile_manage(self, widget):
+ dia = DBListEditor(self.dialog, self.profile_store, self.profiles)
+ dia.run()
+ with open(self.profile_cfg, 'wb') as configfile:
+ self.profiles.write(configfile)
- def db_create(self, widget):
- dia = DBCreate()
- dia.run(self.dialog)
- host = CONFIG['login.server']
- port = int(CONFIG['login.port'])
- DBLogin.refreshlist(None, self.combo_database,
- self.combo_label, self.combo_button, host, port,
- self.button_connect)
- return
+ def profile_changed(self, combobox):
+ position = combobox.get_active()
+ if position == -1:
+ return
+ profile = self.profile_store[position][0]
+ try:
+ username = self.profiles.get(profile, 'username')
+ except ConfigParser.NoOptionError:
+ username = ''
+ if username:
+ self.entry_login.set_text(username)
+ self.entry_password.grab_focus()
+ else:
+ self.entry_login.set_text('')
+ self.entry_login.grab_focus()
- def run(self, dbname, parent):
+ def run(self, profile_name, parent):
+ if not profile_name:
+ selected_profile = CONFIG['login.profile']
+ if not selected_profile:
+ short_version = '.'.join(VERSION.split('.', 2)[:2])
+ selected_profile = 'demo%s.tryton.org' % short_version
+ else:
+ selected_profile = profile_name
+ for idx, row in enumerate(self.profile_store):
+ if row[0] == selected_profile:
+ self.combo_profile.set_active(idx)
+ break
self.dialog.show_all()
- self.combo_label.hide()
- self.combo_button.hide()
-
- if not CONFIG['login.host']:
- self.label_server.hide()
- self.entry_server.hide()
- self.button_server.hide()
-
- host = CONFIG['login.server']
- port = int(CONFIG['login.port'])
-
- url = '%s:%d' % (host, port)
- self.entry_server.set_text(url)
- self.entry_login.set_text(CONFIG['login.login'])
-
- # construct the list of available db and select the last one used
- liststore = gtk.ListStore(str)
- self.combo_database.set_model(liststore)
- cell = gtk.CellRendererText()
- self.combo_database.pack_start(cell, True)
- self.combo_database.add_attribute(cell, 'text', 0)
-
- res = self.refreshlist(None, self.combo_database, self.combo_label,
- self.combo_button, host, port, self.button_connect)
-
- self.button_server.connect_after('clicked', DBLogin.refreshlist_ask,
- self.entry_server, self.combo_database, self.combo_label,
- self.combo_button, self.button_connect, host, port, self.dialog)
- if dbname:
- i = liststore.get_iter_root()
- while i:
- if liststore.get_value(i, 0)==dbname:
- self.combo_database.set_active_iter(i)
- break
- i = liststore.iter_next(i)
# Reshow dialog for gtk-quarks
self.dialog.reshow_with_initial_size()
- res = self.dialog.run()
- url_m = re.match('^([\w.:\-\d]+):(\d{1,5})$',
- self.entry_server.get_text() or '')
- if url_m:
- CONFIG['login.server'] = url_m.group(1)
- CONFIG['login.login'] = self.entry_login.get_text()
- CONFIG['login.port'] = url_m.group(2)
- CONFIG['login.db'] = self.combo_database.get_active_text()
- result = (self.entry_login.get_text(),
- self.entry_password.get_text(), url_m.group(1),
- int(url_m.group(2)), self.combo_database.get_active_text())
- else:
- parent.present()
- self.dialog.destroy()
- raise Exception('QueryCanceled')
+
+ res, result = None, ('', '', '', '', '')
+ while not (res in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT)
+ or (res == gtk.RESPONSE_OK and all(result))):
+ res = self.dialog.run()
+ active_profile = self.combo_profile.get_active()
+ if active_profile != -1:
+ profile = self.profile_store[active_profile][0]
+ CONFIG['login.profile'] = profile
+ try:
+ result = (self.entry_login.get_text(),
+ self.entry_password.get_text(),
+ self.profiles.get(profile, 'host'),
+ int(self.profiles.get(profile, 'port')),
+ self.profiles.get(profile, 'database'))
+ except ConfigParser.NoOptionError, e:
+ pass
+
if res != gtk.RESPONSE_OK:
parent.present()
self.dialog.destroy()
diff --git a/tryton/gui/window/dbrestore.py b/tryton/gui/window/dbrestore.py
index 07a54fc702..77ccdf83ae 100644
--- a/tryton/gui/window/dbrestore.py
+++ b/tryton/gui/window/dbrestore.py
@@ -173,16 +173,7 @@ class DBRestore(object):
self.dialog.set_transient_for(parent)
self.dialog.show_all()
- if not CONFIG['login.host']:
- self.label_server_url.hide()
- self.entry_server_url.hide()
- self.button_server_change.hide()
- self.label_server_password.hide()
- self.entry_server_password.hide()
- self.hseparator.hide()
-
- url = '%s:%d' % (CONFIG['login.server'], int(CONFIG['login.port']))
- self.entry_server_url.set_text(url)
+ self.entry_server_url.set_text('')
while True:
database = False
passwd = False
diff --git a/tryton/rpc.py b/tryton/rpc.py
index 7e14218a61..3368588eac 100644
--- a/tryton/rpc.py
+++ b/tryton/rpc.py
@@ -49,9 +49,12 @@ def db_list(host, port):
_SEMAPHORE.release()
logging.getLogger('rpc.result').debug(repr(res))
return res
- except Exception:
- logging.getLogger('rpc.result').debug(repr(None))
- return None
+ except Exception, exception:
+ if exception[0] == 'AccessDenied':
+ raise
+ else:
+ logging.getLogger('rpc.result').debug(repr(None))
+ return None
def db_exec(host, port, method, *args):
global _SOCK, SECURE