diff --git a/share/tryton/tryton.glade b/share/tryton/tryton.glade index e3c4caf1bc..11b77ffed2 100644 --- a/share/tryton/tryton.glade +++ b/share/tryton/tryton.glade @@ -1746,216 +1746,6 @@ Latin1 - - Choose a Database... - GTK_WIN_POS_CENTER_ON_PARENT - GDK_WINDOW_TYPE_HINT_DIALOG - - - True - - - True - 3 - 3 - - - True - 0 - <b>Backup a Database</b> - True - - - False - False - - - - - True - - - False - False - 1 - - - - - True - 2 - 3 - 3 - 3 - 3 - - - True - 1 - 3 - 3 - Tryton Server Password: - GTK_JUSTIFY_RIGHT - - - 1 - 2 - GTK_FILL - - - - - - True - 1 - Database: - - - 2 - 3 - GTK_FILL - - - - - - True - - - True - - - - - True - 0 - True - - - False - False - 1 - - - - - 1 - 3 - 2 - 3 - GTK_FILL - GTK_FILL - - - - - True - True - True - False - True - 16 - - - 1 - 3 - 1 - 2 - - - - - - 117 - True - 1 - 3 - 3 - Server: - - - GTK_FILL - - - - - - True - True - Change - 0 - - - - 2 - 3 - GTK_FILL - - - - - - True - False - False - True - 16 - http://localhost:8069 - - - 1 - 2 - - - - - - False - False - 2 - - - - - 2 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-cancel - True - -6 - - - - - True - True - True - gtk-ok - True - -5 - - - 1 - - - - - False - GTK_PACK_END - - - - - Restore a Database GTK_WIN_POS_CENTER_ON_PARENT diff --git a/tryton/gui/main.py b/tryton/gui/main.py index 53614c2171..57de322a36 100644 --- a/tryton/gui/main.py +++ b/tryton/gui/main.py @@ -14,6 +14,7 @@ from tryton.gui.window.preference import Preference from tryton.gui.window import FilesActions from tryton.gui.window.dblogin import DBLogin from tryton.gui.window.dbcreate import DBCreate +from tryton.gui.window.dbdumpdrop import DBBackupDrop from tryton.gui.window.tips import Tips from tryton.gui.window.about import About from tryton.gui.window.shortcuts import Shortcuts @@ -1273,24 +1274,35 @@ class Main(object): def sig_db_drop(self, widget): if not self.sig_logout(widget): return False - url, dbname, passwd = self._choose_db_select(_('Delete a database')) + dialog = DBBackupDrop(self.window, function='drop') + url, dbname, passwd = dialog.run(self.window) if not dbname: rpc.logout() Main.get_main().refresh_ssl() return host, port = url.rsplit(':', 1) - + sure = common.sur_3b(_("You are going to delete a Tryton " \ + "database.\nAre you really sure to proceed?"), self.window) + if sure == "ko" or sure == "cancel": + return try: rpc.db_exec(host, int(port), 'drop', passwd, dbname) except Exception, exception: self.refresh_ssl() - common.warning(_('Database drop failed with '\ - 'error message:\n') + str(exception[0]), self.window, - _('Database drop failed!')) + if exception[0] == "AccessDenied": + common.warning(_("The Tryton Server Password you provided " \ + "is different to the stored password in the " \ + "configuration.\n\nPlease try again."), \ + self.window, _('Access denied!')) + self.sig_db_drop(self.window) + else: + common.warning(_('Database drop failed with ' \ + 'error message:\n') + str(exception[0]), \ + self.window, _('Database drop failed!')) return self.refresh_ssl() - common.message(_("Database dropped successfully!"), + common.message(_("Database dropped successfully!"), \ parent=self.window) def sig_db_restore(self, widget): @@ -1374,8 +1386,10 @@ class Main(object): win.destroy() def sig_db_dump(self, widget): - url, dbname, passwd = self._choose_db_select(_('Backup a database')) - if not dbname: + dialog = DBBackupDrop(self.window, function='backup') + url, dbname, passwd = dialog.run(self.window) + + if not (dbname and url and passwd): rpc.logout() Main.get_main().refresh_ssl() return @@ -1384,105 +1398,45 @@ class Main(object): try: dump_b64 = rpc.db_exec(host, int(port), 'dump', passwd, dbname) except Exception, exception: + if exception[0] == "Couldn't dump database with password": + common.warning(_("It is impossible to dump a password " \ + "protected Database.\nBackground: \n" \ + "The Postgres database backup and restore " \ + "functions 'pg_dump' and 'pg_restore' did not " \ + "support passwords at all. Backup and restore " \ + "needed to be proceed manual."), \ + self.window, _('Database is password protected!')) + elif exception[0] == "AccessDenied": + common.warning(_("The Tryton Server Password you provided " \ + "is different to the stored password in the " \ + "configuration.\n\nPlease try again."), \ + self.window, _('Access denied!')) + self.sig_db_dump(self.window) + else: + common.warning(_('Database dump failed with ' \ + 'error message:\n') + str(exception[0]), \ + self.window, _('Database dump failed!')) rpc.logout() Main.get_main().refresh_ssl() - common.warning(_('Database dump failed with error message:\n') + \ - str(exception[0]), self.window, _('Database dump failed!')) return + self.refresh_ssl() dump = base64.decodestring(dump_b64) - filename = common.file_selection(_('Save As...'), - action=gtk.FILE_CHOOSER_ACTION_SAVE, parent=self.window, + filename = common.file_selection(_('Save As...'), \ + action=gtk.FILE_CHOOSER_ACTION_SAVE, parent=self.window, \ preview=False) if filename: file_ = file(filename, 'wb') file_.write(dump) file_.close() - common.message(_("Database backuped successfully!"), + common.message(_("Database backuped successfully!"), \ parent=self.window) else: rpc.logout() Main.get_main().refresh_ssl() - def _choose_db_select(self, title=_("Backup a database")): - 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: - label.set_label('' + _('Could not connect to server!') +\ - '') - else: - label.set_label('' + \ - _('Incompatible version of the server!') + '') - db_widget.hide() - label.show() - elif res == 0: - label.set_label('' + \ - _('No database found, you must create one!') + '') - db_widget.hide() - label.show() - else: - label.hide() - db_widget.show() - return res - - def refreshlist_ask(widget, server_widget, db_widget, label, - parent=None): - res = common.request_server(server_widget, parent) - if not res: - return None - host, port = res - refreshlist(widget, db_widget, label, host, port) - return (host, port) - - dialog = glade.XML(GLADE, "win_db_select", - gettext.textdomain()) - win = dialog.get_widget('win_db_select') - win.set_icon(TRYTON_ICON) - win.set_default_response(gtk.RESPONSE_OK) - win.set_transient_for(self.window) - win.show_all() - - pass_widget = dialog.get_widget('ent_passwd_select') - server_widget = dialog.get_widget('ent_server_select') - db_widget = dialog.get_widget('combo_db_select') - label = dialog.get_widget('label_db_select') - - - dialog.get_widget('db_select_label').set_markup(''+title+'') - - host = CONFIG['login.server'] - port = int(CONFIG['login.port']) - url = '%s:%d' % (host, port) - server_widget.set_text(url) - - liststore = gtk.ListStore(str) - db_widget.set_model(liststore) - - refreshlist(None, db_widget, label, host, port) - change_button = dialog.get_widget('but_server_select') - change_button.connect_after('clicked', refreshlist_ask, - server_widget, db_widget, label, win) - - cell = gtk.CellRendererText() - db_widget.pack_start(cell, True) - db_widget.add_attribute(cell, 'text', 0) - - res = win.run() - - database = False - url = False - passwd = False - if res == gtk.RESPONSE_OK: - database = db_widget.get_active_text() - url = server_widget.get_text() - passwd = pass_widget.get_text() - self.window.present() - win.destroy() - return (url, database, passwd) - def _choose_db_ent(self): dialog = glade.XML(GLADE, "win_db_ent", gettext.textdomain()) diff --git a/tryton/gui/window/dbdumpdrop.py b/tryton/gui/window/dbdumpdrop.py new file mode 100644 index 0000000000..8d15576d8a --- /dev/null +++ b/tryton/gui/window/dbdumpdrop.py @@ -0,0 +1,225 @@ +# 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 gtk +import gettext +import tryton.common as common +from tryton.config import CONFIG, TRYTON_ICON +import tryton.rpc as rpc + +_ = gettext.gettext + +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: + label.set_label('' + \ + _('Could not connect to server!') + '') + else: + label.set_label('' + \ + _('Incompatible version of the server!') + '') + db_widget.hide() + label.show() + elif res == 0: + label.set_label('' + \ + _('No database found, you must create one!') + '') + db_widget.hide() + label.show() + else: + label.hide() + db_widget.show() + return res + + @staticmethod + def refreshlist_ask(widget, server_widget, db_widget, label, parent=None): + res = common.request_server(server_widget, parent) + if not res: + return None + host, port = res + DBBackupDrop.refreshlist(widget, db_widget, label, host, port) + return (host, port) + + def event_show_button_ok(self, widget, event, data=None): + """ + This event method decide by rules if the Create button will be + sensitive or insensitive. The general rule is, all given fields + must be filled, then the Create button is set to sensitive. This + event method doesn't check the valid of single entrys. + """ + if self.entry_server_connection.get_text() != "" \ + and self.combo_database.get_active() != -1 \ + and self.entry_serverpasswd.get_text() != "": + widget.unset_flags(gtk.HAS_DEFAULT) + self.button_ok.set_sensitive(True) + self.button_ok.set_flags(gtk.CAN_DEFAULT) + self.button_ok.set_flags(gtk.HAS_DEFAULT) + self.button_ok.set_flags(gtk.CAN_FOCUS) + self.button_ok.set_flags(gtk.RECEIVES_DEFAULT) + self.button_ok.grab_default() + else: + self.button_ok.set_sensitive(False) + + def __init__(self, parent, function=None): + # This widget is used for creating and droping a database! + if function =="backup": + dialog_title = _("Backup a database") + button_ok_text = _("Backup") + button_ok_tooltip = _("Backup the choosen database.") + button_ok_icon = "tryton-save-as" + label_subtitle_text = _("Choose a Tryton database to backup:") + elif function == "drop": + dialog_title = _("Delete a database") + button_ok_text = _("Delete") + button_ok_tooltip = _("Delete the choosen database.") + button_ok_icon = "tryton-delete" + label_subtitle_text = _("Choose a Tryton database to delete:") + else: + return None + + self.dialog = gtk.Dialog( \ + title = dialog_title, \ + parent = parent, \ + flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT \ + | gtk.WIN_POS_CENTER_ON_PARENT,) + self.dialog.set_has_separator(True) + self.dialog.set_size_request(500, 201) + self.dialog.set_icon(TRYTON_ICON) + self.dialog.connect("key-press-event", self.event_show_button_ok) + self.tooltips = gtk.Tooltips() + self.dialog.add_button("gtk-cancel", \ + gtk.RESPONSE_CANCEL) + self.button_ok = gtk.Button(button_ok_text) + self.button_ok.set_flags(gtk.CAN_DEFAULT) + self.button_ok.set_flags(gtk.HAS_DEFAULT) + self.button_ok.set_sensitive(False) + img_connect = gtk.Image() + img_connect.set_from_stock(button_ok_icon, gtk.ICON_SIZE_BUTTON) + self.button_ok.set_image(img_connect) + self.tooltips.set_tip(self.button_ok, button_ok_tooltip) + self.dialog.add_action_widget(self.button_ok, gtk.RESPONSE_OK) + self.dialog.set_default_response(gtk.RESPONSE_OK) + self.dialog_vbox = gtk.VBox() + + table = gtk.Table(5, 3, False) + table.set_border_width(10) + table.set_row_spacings(3) + table.set_col_spacings(3) + self.dialog_vbox.pack_start(table, True, True,0) + + label_subtitle = gtk.Label() + label_subtitle.set_markup(""+ label_subtitle_text + "") + label_subtitle.set_justify(gtk.JUSTIFY_LEFT) + label_subtitle.set_alignment(0, 1) + label_subtitle.set_padding( 9, 5) + table.attach(label_subtitle, 0, 3, 0, 1, xoptions=gtk.FILL) + + hseparator = gtk.HSeparator() + table.attach(hseparator, 0, 3, 1, 2) + + label_server = gtk.Label(_("Server Connection:")) + label_server.set_alignment(1, 0.5) + label_server.set_padding(3, 3) + table.attach(label_server, 0, 1, 2, 3, xoptions=gtk.FILL) + + self.entry_server_connection = gtk.Entry() + self.entry_server_connection.set_sensitive(False) + self.entry_server_connection.unset_flags(gtk.CAN_FOCUS) + self.entry_server_connection.set_editable(False) + self.entry_server_connection.set_text("http://localhost:8070") + 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 change the address."), None) + table.attach(self.entry_server_connection, 1, 2, 2, 3) + + self.button_server_change = gtk.Button(_("C_hange"), stock=None, \ + use_underline=True) + img_button_server_change = gtk.Image() + img_button_server_change.set_from_stock('tryton-preferences-system', \ + gtk.ICON_SIZE_BUTTON) + self.button_server_change.set_image(img_button_server_change) + table.attach(self.button_server_change, 2, 3, 2, 3, yoptions=False, \ + xoptions=gtk.FILL) + + self.label_database = gtk.Label() + self.label_database.set_text(_("Database:")) + self.label_database.set_alignment(1, 0.5) + self.label_database.set_padding(3, 3) + table.attach(self.label_database, 0, 1, 3, 4, xoptions=gtk.FILL) + + vbox_combo = gtk.VBox() + self.combo_database = gtk.ComboBox() + 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) + table.attach(vbox_combo, 1, 2, 3, 4) + + label_serverpasswd = gtk.Label(_("Tryton Server Password:")) + label_serverpasswd.set_justify(gtk.JUSTIFY_RIGHT) + label_serverpasswd.set_alignment(1, 0.5) + label_serverpasswd.set_padding( 3, 3) + table.attach(label_serverpasswd, 0, 1, 4, 5, xoptions=gtk.FILL) + + self.entry_serverpasswd = gtk.Entry() + self.entry_serverpasswd.set_visibility(False) + self.entry_serverpasswd.set_activates_default(True) + self.tooltips.set_tip(self.entry_serverpasswd, _("This is the " \ + "password of the Tryton server. It doesn't belong to a " \ + "real user. This password is usually defined in the trytond " \ + "configuration."), None) + table.attach(self.entry_serverpasswd, 1, 2, 4, 5) + + self.entry_serverpasswd.grab_focus() + self.dialog.vbox.pack_start(self.dialog_vbox) + + def run(self, parent): + self.dialog.set_default_response(gtk.RESPONSE_OK) + self.dialog.set_transient_for(parent) + self.dialog.show_all() + + pass_widget = self.entry_serverpasswd + server_widget = self.entry_server_connection + db_widget = self.combo_database + label = self.combo_database_label + + host = CONFIG['login.server'] + port = int(CONFIG['login.port']) + url = '%s:%d' % (host, port) + server_widget.set_text(url) + + 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) + + change_button = self.button_server_change + change_button.connect_after('clicked', DBBackupDrop.refreshlist_ask, \ + server_widget, db_widget, label, self.dialog) + + while True: + database = False + url = False + passwd = False + res = self.dialog.run() + if res == gtk.RESPONSE_OK: + database = db_widget.get_active_text() + url = server_widget.get_text() + passwd = pass_widget.get_text() + break + if res != gtk.RESPONSE_OK: + self.dialog.destroy() + rpc.logout() + break + parent.present() + self.dialog.destroy() + + return (url, database, passwd) +