Commit c9e23a09 authored by Philipp Hörist's avatar Philipp Hörist

Make remove account dialog an Assistant

parent c35ba59a
Pipeline #4671 passed with stages
in 3 minutes and 6 seconds
......@@ -438,9 +438,6 @@ class Connection(CommonConnection, ConnectionHandlers):
self.new_account_form = None
self.last_sent = []
self._unregister_account = False
self._unregister_account_cb = None
self.music_track_info = 0
self.register_supported = False
......@@ -541,7 +538,7 @@ class Connection(CommonConnection, ConnectionHandlers):
log.info('Starting to disconnect %s', self.name)
disconnect_cb = partial(self._on_disconnect, reconnect)
self.connection.disconnect_handlers = [disconnect_cb]
if immediately:
if immediately or self.removing_account:
self.connection.disconnect()
else:
self.connection.start_disconnect()
......@@ -562,7 +559,7 @@ class Connection(CommonConnection, ConnectionHandlers):
log.info('Disconnect %s, reconnect: %s', self.name, reconnect)
if reconnect:
if reconnect and not self.removing_account:
if app.account_is_connected(self.name):
# we cannot change our status to offline or connecting
# after we auth to server
......@@ -1263,10 +1260,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self._register_new_handlers(con)
def _on_auth_successful(self):
if self._unregister_account:
self._on_unregister_account_connect()
else:
self.connection.bind()
self.connection.bind()
def _on_auth_failed(self, reason, text):
if not app.config.get_per('accounts', self.name, 'savepass'):
......@@ -1675,41 +1669,12 @@ class Connection(CommonConnection, ConnectionHandlers):
stanza_id=obj.stanza_id,
additional_data=obj.additional_data))
def unregister_account(self, on_remove_success):
self._unregister_account = True
self._unregister_account_cb = on_remove_success
if self.connected == 0:
self.connect_and_auth()
else:
self._on_unregister_account_connect()
def _on_unregister_account_connect(self):
self.removing_account = True
if app.account_is_connected(self.name):
hostname = app.config.get_per('accounts', self.name, 'hostname')
iq = nbxmpp.Iq(typ='set', to=hostname)
id_ = self.connection.getAnID()
iq.setID(id_)
iq.setTag(nbxmpp.NS_REGISTER + ' query').setTag('remove')
def _on_answer(con, result):
if result.getID() == id_:
self._on_unregister_finished(True)
return
app.nec.push_incoming_event(InformationEvent(
None, dialog_name='unregister-error',
kwargs={'server': hostname, 'error': result.getErrorMsg()}))
self._on_unregister_finished(False)
self.connection.RegisterHandler(
'iq', _on_answer, 'result', system=True)
self.connection.SendAndWaitForResponse(iq)
def unregister_account(self, callback):
if not app.account_is_connected(self.name):
return
self._on_unregister_finished(False)
self.removing_account = False
def _on_unregister_finished(self, result):
self._unregister_account_cb(result)
self._unregister_account = False
self._unregister_account_cb = None
self.removing_account = True
self.connection.get_module('Register').unregister(
callback=callback)
def _reconnect_alarm(self):
if not app.config.get_per('accounts', self.name, 'active'):
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="remove_account_window">
<property name="can_focus">False</property>
<property name="border_width">18</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
<signal name="destroy" handler="_on_destroy" swapped="no"/>
<child>
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">What do you want to do?</property>
<property name="use_markup">True</property>
<style>
<class name="large-header"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">user-trash-symbolic</property>
<property name="icon_size">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="remove_only_radiobutton">
<property name="label" translatable="yes">Remove account _only from Gajim</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="remove_and_unregister_radiobutton">
<property name="label" translatable="yes">Remove account from Gajim and from _server</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">remove_only_radiobutton</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">12</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label">_Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_cancel_button_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="remove_button">
<property name="label" translatable="yes">_Remove</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_remove_button_clicked" swapped="no"/>
<style>
<class name="destructive-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
......@@ -88,11 +88,6 @@ messages = {
_('This account %s doesn\'t support invisibility.'),
ErrorDialog),
'unregister-error': Message(
_('Unregister Failed'),
_('Unregistration on server %(server)s failed: %(error)s'),
ErrorDialog),
'agent-register-success': Message(
_('Registration Succeeded'),
_('Registration with agent %s succeeded.'),
......
......@@ -31,16 +31,12 @@ from gajim import gui_menu_builder
from gajim.gtk.dialogs import DialogButton
from gajim.gtk.dialogs import NewConfirmationDialog
from gajim.gtk.dialogs import ConfirmationDialogDoubleRadio
from gajim.gtk.dialogs import ErrorDialog
from gajim.gtk.dialogs import PassphraseDialog
from gajim.gtk.const import Setting
from gajim.gtk.const import SettingKind
from gajim.gtk.const import SettingType
from gajim.gtk.settings import SettingsDialog
from gajim.gtk.settings import SettingsBox
from gajim.gtk.util import get_builder
from gajim.gtk.util import get_app_window
from gajim.gtk.util import open_window
log = logging.getLogger('gajim.gtk.accounts')
......@@ -159,7 +155,8 @@ class AccountsWindow(Gtk.ApplicationWindow):
values.append(app.config.get_per('accounts', account, setting))
return values
def on_remove_account(self, account):
@staticmethod
def on_remove_account(account):
if app.events.get_events(account):
app.interface.raise_dialog('unread-events-on-remove-account')
return
......@@ -168,39 +165,7 @@ class AccountsWindow(Gtk.ApplicationWindow):
# Should never happen as button is insensitive
return
win_opened = False
if app.interface.msg_win_mgr.get_controls(acct=account):
win_opened = True
elif account in app.interface.instances:
for key in app.interface.instances[account]:
if (app.interface.instances[account][key] and
key != 'remove_account'):
win_opened = True
break
# Detect if we have opened windows for this account
def remove():
if (account in app.interface.instances and
'remove_account' in app.interface.instances[account]):
dialog = app.interface.instances[account]['remove_account']
dialog.window.present()
else:
if account not in app.interface.instances:
app.interface.instances[account] = {}
app.interface.instances[account]['remove_account'] = \
RemoveAccountWindow(account)
if win_opened:
NewConfirmationDialog(
_('Remove Account'),
_('You still have open chats in your account %s') % account,
_('All chat and group chat windows will be closed.'),
[DialogButton.make('Cancel'),
DialogButton.make('Remove',
callback=remove)],
transient_for=self).show()
else:
remove()
open_window('RemoveAccount', account=account)
def remove_account(self, account):
del self._need_relogin[account]
......@@ -909,138 +874,3 @@ class LoginDialog(SettingsDialog):
savepass = app.config.get_per('accounts', self.account, 'savepass')
if not savepass:
passwords.delete_password(self.account)
class RemoveAccountWindow:
"""
Ask whether to remove from gajim only or both from gajim and the server,
then remove the account given
"""
def _on_destroy(self, _widget):
if self.account in app.interface.instances:
del app.interface.instances[self.account]['remove_account']
def on_cancel_button_clicked(self, _widget):
self._ui.remove_account_window.destroy()
def __init__(self, account):
self.account = account
self._ui = get_builder('remove_account_window.ui')
active_window = app.app.get_active_window()
self._ui.remove_account_window.set_transient_for(active_window)
self._ui.remove_account_window.set_title(
_('Removing account %s') % self.account)
self._ui.connect_signals(self)
self._ui.remove_account_window.show_all()
def on_remove_button_clicked(self, _widget):
def remove():
if self.account in app.connections and \
app.connections[self.account].connected and \
not self._ui.remove_and_unregister_radiobutton.get_active():
# change status to offline only if we will not
# remove this JID from server
app.connections[self.account].change_status('offline',
'offline')
if self._ui.remove_and_unregister_radiobutton.get_active():
if not self.account in app.connections:
ErrorDialog(
_('Account is disabled'),
_('To unregister from a server, the account must be '
'enabled.'),
transient_for=self._ui.remove_account_window)
return
if not app.connections[self.account].password:
def on_ok(passphrase, _checked):
if passphrase == -1:
# We don't remove account cause we
# canceled pw window
return
app.connections[self.account].password = passphrase
app.connections[self.account].unregister_account(
self._on_remove_success)
PassphraseDialog(
_('Password required'),
_('Enter your password for account %s') % self.account,
_('Save password'), ok_handler=on_ok,
transient_for=self._ui.remove_account_window)
return
app.connections[self.account].unregister_account(
self._on_remove_success)
else:
self._on_remove_success(True)
if (self.account in app.connections and
app.connections[self.account].connected):
NewConfirmationDialog(
_('Remove Account'),
_('Still connected to the server'),
_('Account \'%s\' is still connected to the server. If you '
'remove it, the connection will be lost.') % self.account,
[DialogButton.make('Cancel'),
DialogButton.make('Remove',
callback=remove)],
transient_for=self._ui.remove_account_window).show()
else:
remove()
def on_remove_response_ok(self, is_checked):
if is_checked[0]:
self._on_remove_success(True)
def _on_remove_success(self, res):
# action of unregistration has failed, we don't remove the account
# Error message is send by connect_and_auth()
if not res:
ConfirmationDialogDoubleRadio(
_('Connection to server %s failed') % self.account,
_('What would you like to do?'),
_('Remove only from Gajim'),
_('Don\'t remove anything. I\'ll try again later'),
on_response_ok=self.on_remove_response_ok, is_modal=False,
transient_for=self._ui.remove_account_window)
return
# Close all opened windows
app.interface.roster.close_all(self.account, force=True)
if self.account in app.connections:
app.connections[self.account].disconnect(reconnect=False)
app.connections[self.account].cleanup()
del app.connections[self.account]
app.logger.remove_roster(app.get_jid_from_account(self.account))
# Delete password must be before del_per() because it calls set_per()
# which would recreate the account with defaults values if not found
passwords.delete_password(self.account)
app.config.del_per('accounts', self.account)
del app.interface.instances[self.account]
if self.account in app.nicks:
del app.interface.minimized_controls[self.account]
del app.nicks[self.account]
del app.block_signed_in_notifications[self.account]
del app.groups[self.account]
app.contacts.remove_account(self.account)
del app.gc_connected[self.account]
del app.automatic_rooms[self.account]
del app.to_be_removed[self.account]
del app.newly_added[self.account]
del app.sleeper_state[self.account]
del app.last_message_time[self.account]
del app.status_before_autoaway[self.account]
del app.gajim_optional_features[self.account]
del app.caps_hash[self.account]
if len(app.connections) >= 2: # Do not merge accounts if only one exists
app.interface.roster.regroup = app.config.get('mergeaccounts')
else:
app.interface.roster.regroup = False
app.interface.roster.setup_and_draw_roster()
app.app.remove_account_actions(self.account)
gui_menu_builder.build_accounts_menu()
window = get_app_window('AccountsWindow')
if window is not None:
window.remove_account(self.account)
self._ui.remove_account_window.destroy()
def destroy(self):
self._ui.remove_account_window.destroy()
......@@ -129,4 +129,5 @@ WINDOW_MODULES = {
'AdvancedConfig': 'gajim.gtk.advanced_config',
'CertificateDialog': 'gajim.gtk.dialogs',
'SubscriptionRequest': 'gajim.gtk.subscription_request',
'RemoveAccount': 'gajim.gtk.remove_account',
}
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import logging
from gi.repository import Gtk
from nbxmpp.util import is_error_result
from gajim.common import app
from gajim.common.i18n import _
from gajim.common.helpers import to_user_string
from gajim.gtk.assistant import Assistant
from gajim.gtk.assistant import Page
from gajim.gtk.util import ensure_not_destroyed
log = logging.getLogger('gajim.gtk.remove_account')
class RemoveAccount(Assistant):
def __init__(self, account):
Assistant.__init__(self)
self.account = account
self._con = app.connections.get(account)
self._destroyed = False
self.add_button('remove', _('Remove'), 'destructive-action')
self.add_button('close', _('Close'))
self.add_button('back', _('Back'))
self.add_pages({'remove_choice': RemoveChoice(account)})
progress = self.add_default_page('progress')
progress.set_title(_('Removing Account...'))
progress.set_text(_('Trying to remove account...'))
success = self.add_default_page('success')
success.set_title(_('Account Removed'))
success.set_heading(_('Account Removed'))
success.set_text(
_('Your account has has been unregistered successfully.'))
error = self.add_default_page('error')
error.set_title(_('Account Removal Failed'))
error.set_heading(_('Account Removal Failed'))
self.set_button_visible_func(self._visible_func)
self.connect('button-clicked', self._on_button_clicked)
self.connect('page-changed', self._on_page_changed)
self.connect('destroy', self._on_destroy)
self.show_all()
@staticmethod
def _visible_func(_assistant, page_name):
if page_name == 'remove_choice':
return ['remove']
if page_name == 'progress':
return None
if page_name == 'error':
return ['back']
if page_name == 'success':
return ['close']
raise ValueError('page %s unknown' % page_name)
def _on_button_clicked(self, _assistant, button_name):
page = self.get_current_page()
if button_name == 'remove':
if page == 'remove_choice':
self.show_page('progress', Gtk.StackTransitionType.SLIDE_LEFT)
self._on_remove()
return
if button_name == 'back':
if page == 'error':
self.show_page('remove_choice',
Gtk.StackTransitionType.SLIDE_RIGHT)
return
if button_name == 'close':
self.destroy()
def _on_page_changed(self, _assistant, page_name):
if page_name == 'remove_choice':
self.set_default_button('remove')
elif page_name == 'success':
self.set_default_button('close')
elif page_name == 'error':
self.set_default_button('back')
def _on_remove(self, *args):
if self.get_page('remove_choice').is_remove_from_server():
self._con.unregister_account(self._on_remove_response)
else:
if app.account_is_connected(self.account):
self._con.change_status('offline', 'offline')
app.interface.remove_account(self.account)
self.destroy()
@ensure_not_destroyed
def _on_remove_response(self, result):
if is_error_result(result):
self._con.removing_account = False
error_text = to_user_string(result)
self.get_page('error').set_text(error_text)
self.show_page('error')
else:
app.interface.remove_account(self.account)
self.show_page('success')
def _on_destroy(self, *args):
self._destroyed = True
class RemoveChoice(Page):
def __init__(self, account):
Page.__init__(self)
self.title = _('Remove Account')
heading = Gtk.Label(label=_('Remove Account'))
heading.get_style_context().add_class('large-header')
heading.set_max_width_chars(30)
heading.set_line_wrap(True)
heading.set_halign(Gtk.Align.CENTER)
heading.set_justify(Gtk.Justification.CENTER)
label = Gtk.Label(label=_('This will remove your account from Gajim.'))
label.set_max_width_chars(50)
label.set_line_wrap(True)
label.set_halign(Gtk.Align.CENTER)
label.set_justify(Gtk.Justification.CENTER)
service = app.connections[account].get_own_jid().getDomain()
check_label = Gtk.Label()
check_label.set_markup(
_('Do you want to unregister your account on <b>%s</b> as '
'well?') % service)
check_label.set_max_width_chars(50)
check_label.set_line_wrap(True)
check_label.set_halign(Gtk.Align.CENTER)
check_label.set_justify(Gtk.Justification.CENTER)
check_label.set_margin_top(40)
self._server = Gtk.CheckButton.new_with_mnemonic(
_('_Unregister account from service'))
self._server.set_halign(Gtk.Align.CENTER)
self.pack_start(heading, False, True, 0)
self.pack_start(label, False, True, 0)
self.pack_start(check_label, False, True, 0)
self.pack_start(self._server, False, True, 0)
self.show_all()
def is_remove_from_server(self):
return self._server.get_active()
......@@ -109,6 +109,7 @@ from gajim.gtk.roster_item_exchange import RosterItemExchangeWindow
from gajim.gtk.util import get_show_in_roster
from gajim.gtk.util import get_show_in_systray
from gajim.gtk.util import open_window
from gajim.gtk.util import get_app_window
from gajim.gtk.const import ControlType
......@@ -1797,6 +1798,46 @@ class Interface:
self.roster.setup_and_draw_roster()
gui_menu_builder.build_accounts_menu()
def remove_account(self, account):
# Close all opened windows
self.roster.close_all(account, force=True)
if account in app.connections:
app.connections[account].disconnect(reconnect=False)
app.connections[account].cleanup()
del app.connections[account]
app.logger.remove_roster(app.get_jid_from_account(account))
# Delete password must be before del_per() because it calls set_per()
# which would recreate the account with defaults values if not found
passwords.delete_password(account)
app.config.del_per('accounts', account)
del self.instances[account]
if account in app.nicks:
del self.minimized_controls[account]
del app.nicks[account]
del app.block_signed_in_notifications[account]
del app.groups[account]
app.contacts.remove_account(account)
del app.gc_connected[account]
del app.automatic_rooms[account]
del app.to_be_removed[account]
del app.newly_added[account]
del app.sleeper_state[account]
del app.last_message_time[account]
del app.status_before_autoaway[account]
del app.gajim_optional_features[account]
del app.caps_hash[account]
if len(app.connections) >= 2: # Do not merge accounts if only one exists
self.roster.regroup = app.config.get('mergeaccounts')
else:
self.roster.regroup = False
self.roster.setup_and_draw_roster()
app.app.remove_account_actions(account)
gui_menu_builder.build_accounts_menu()
window = get_app_window('AccountsWindow')
if window is not None:
window.remove_account(account)
def autoconnect(self):
"""
Auto connect at startup
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment