Make Service Registration a Gtk.Assistant

- Move all xmpp Register methods into the register module
This commit is contained in:
Philipp Hörist 2018-08-04 19:47:25 +02:00
parent 99ec800856
commit 043e764896
13 changed files with 287 additions and 230 deletions

View file

@ -792,11 +792,6 @@ class Connection(CommonConnection, ConnectionHandlers):
None, dialog_name='invalid-answer', None, dialog_name='invalid-answer',
kwargs={'name': data[0], 'error': data[3]})) kwargs={'name': data[0], 'error': data[3]}))
return return
is_form = data[2]
conf = data[1]
app.nec.push_incoming_event(RegisterAgentInfoReceivedEvent(
None, conn=self, agent=data[0], config=conf,
is_form=is_form))
def _select_next_host(self, hosts): def _select_next_host(self, hosts):
""" """

View file

@ -54,61 +54,6 @@ log = logging.getLogger('gajim.c.connection_handlers')
# kind of events we can wait for an answer # kind of events we can wait for an answer
AGENT_REMOVED = 'agent_removed' AGENT_REMOVED = 'agent_removed'
class ConnectionDisco:
def request_register_agent_info(self, agent):
if not self.connection or self.connected < 2:
return None
iq = nbxmpp.Iq('get', nbxmpp.NS_REGISTER, to=agent)
id_ = self.connection.getAnID()
iq.setID(id_)
# Wait the answer during 30 secondes
self.awaiting_timeouts[app.idlequeue.current_time() + 30] = (id_,
_('Registration information for transport %s has not arrived in '
'time') % agent)
self.connection.SendAndCallForResponse(iq, self._ReceivedRegInfo,
{'agent': agent})
def _agent_registered_cb(self, con, resp, agent):
if resp.getType() == 'result':
app.nec.push_incoming_event(InformationEvent(
None, dialog_name='agent-register-success', args=agent))
self.get_module('Presence').subscribe(agent, auto_auth=True)
self.agent_registrations[agent]['roster_push'] = True
if self.agent_registrations[agent]['sub_received']:
p = nbxmpp.Presence(agent, 'subscribed')
p = self.add_sha(p)
self.connection.send(p)
if resp.getType() == 'error':
app.nec.push_incoming_event(InformationEvent(
None, dialog_name='agent-register-error',
kwargs={'agent': agent,
'error': resp.getError(),
'error_msg': resp.getErrorMsg()}))
def register_agent(self, agent, info, is_form=False):
if not self.connection or self.connected < 2:
return
if is_form:
iq = nbxmpp.Iq('set', nbxmpp.NS_REGISTER, to=agent)
query = iq.setQuery()
info.setAttr('type', 'submit')
query.addChild(node=info)
self.connection.SendAndCallForResponse(iq,
self._agent_registered_cb, {'agent': agent})
else:
# fixed: blocking
nbxmpp.features_nb.register(self.connection, agent, info,
self._agent_registered_cb, {'agent': agent})
self.agent_registrations[agent] = {'roster_push': False,
'sub_received': False}
def _ReceivedRegInfo(self, con, resp, agent):
nbxmpp.features_nb._ReceivedRegInfo(con, resp, agent)
self._IqCB(con, resp)
# basic connection handlers used here and in zeroconf # basic connection handlers used here and in zeroconf
class ConnectionHandlersBase: class ConnectionHandlersBase:
def __init__(self): def __init__(self):
@ -443,7 +388,7 @@ class ConnectionHandlersBase:
return sess return sess
class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, class ConnectionHandlers(ConnectionSocks5Bytestream,
ConnectionHandlersBase, ConnectionHandlersBase,
ConnectionJingle, ConnectionIBBytestream): ConnectionJingle, ConnectionIBBytestream):
def __init__(self): def __init__(self):

View file

@ -868,10 +868,6 @@ class SignedInEvent(nec.NetworkIncomingEvent):
name = 'signed-in' name = 'signed-in'
base_network_events = [] base_network_events = []
class RegisterAgentInfoReceivedEvent(nec.NetworkIncomingEvent):
name = 'register-agent-info-received'
base_network_events = []
class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'file-request-received' name = 'file-request-received'
base_network_events = [] base_network_events = []

View file

@ -31,6 +31,8 @@ class Register:
self.handlers = [] self.handlers = []
self.agent_registrations = {}
def change_password(self, password, success_cb, error_cb): def change_password(self, password, success_cb, error_cb):
if not app.account_is_connected(self._account): if not app.account_is_connected(self._account):
return return
@ -52,11 +54,87 @@ class Register:
if not nbxmpp.isResultNode(stanza): if not nbxmpp.isResultNode(stanza):
error = stanza.getErrorMsg() error = stanza.getErrorMsg()
log.info('Error: %s', error) log.info('Error: %s', error)
if error_cb() is not None:
error_cb()(error) error_cb()(error)
else: else:
log.info('Password changed') log.info('Password changed')
if success_cb() is not None:
success_cb()() success_cb()()
def register_agent(self, agent, form, is_form, success_cb, error_cb):
if not app.account_is_connected(self._account):
return
weak_success_cb = weakref.WeakMethod(success_cb)
weak_error_cb = weakref.WeakMethod(error_cb)
iq = nbxmpp.Iq('set', nbxmpp.NS_REGISTER, to=agent)
if is_form:
query = iq.setQuery()
form.setAttr('type', 'submit')
query.addChild(node=form)
else:
for field in form.keys():
iq.setTag('query').setTagData(field, form[field])
self._con.connection.SendAndCallForResponse(
iq, self._register_agent_response, {'agent': agent,
'success_cb': weak_success_cb,
'error_cb': weak_error_cb})
self.agent_registrations[agent] = {'roster_push': False,
'sub_received': False}
def _register_agent_response(self, con, stanza, agent,
success_cb, error_cb):
if not nbxmpp.isResultNode(stanza):
error = stanza.getErrorMsg()
log.info('Error: %s', error)
if error_cb() is not None:
error_cb()(error)
return
self._con.get_module('Presence').subscribe(agent, auto_auth=True)
self.agent_registrations[agent]['roster_push'] = True
if self.agent_registrations[agent]['sub_received']:
self._con.get_module('Presence').subscribed(agent)
if success_cb() is not None:
success_cb()()
def get_register_form(self, jid, success_cb, error_cb):
if not app.account_is_connected(self._account):
return
weak_success_cb = weakref.WeakMethod(success_cb)
weak_error_cb = weakref.WeakMethod(error_cb)
iq = nbxmpp.Iq('get', nbxmpp.NS_REGISTER, to=jid)
self._con.connection.SendAndCallForResponse(
iq, self._register_info_response, {'success_cb': weak_success_cb,
'error_cb': weak_error_cb})
def _register_info_response(self, con, stanza, success_cb, error_cb):
if not nbxmpp.isResultNode(stanza):
error = stanza.getErrorMsg()
log.info('Error: %s', error)
if error_cb() is not None:
error_cb()(error)
else:
log.info('Register form received')
form = stanza.getQuery().getTag('x', namespace=nbxmpp.NS_DATA)
is_form = form is not None
if not is_form:
form = {}
for field in stanza.getQueryPayload():
if not isinstance(field, nbxmpp.Node):
continue
form[field.getName()] = field.getData()
if success_cb() is not None:
success_cb()(form, is_form)
def get_instance(*args, **kwargs): def get_instance(*args, **kwargs):
return Register(*args, **kwargs), 'Register' return Register(*args, **kwargs), 'Register'

View file

@ -1486,57 +1486,6 @@ class FakeDataForm(Gtk.Table, object):
self.infos[name] = self.entries[name].get_text() self.infos[name] = self.entries[name].get_text()
return self.infos return self.infos
class ServiceRegistrationWindow:
"""
Class for Service registration window. Window that appears when we want to
subscribe to a service if is_form we use dataforms_widget else we use
service_registarion_window
"""
def __init__(self, service, infos, account, is_form):
self.service = service
self.account = account
self.is_form = is_form
self.xml = gtkgui_helpers.get_gtk_builder('service_registration_window.ui')
self.window = self.xml.get_object('service_registration_window')
self.window.set_transient_for(app.interface.roster.window)
if self.is_form:
dataform = dataforms.ExtendForm(node = infos)
self.data_form_widget = dataforms_widget.DataFormWidget(dataform)
if self.data_form_widget.title:
self.window.set_title('%s - Gajim' % self.data_form_widget.title)
grid = self.xml.get_object('grid')
grid.attach(self.data_form_widget, 0, 0, 2, 1)
else:
if 'registered' in infos:
self.window.set_title(_('Edit %s') % service)
else:
self.window.set_title(_('Register to %s') % service)
self.data_form_widget = FakeDataForm(infos)
grid = self.xml.get_object('grid')
grid.attach(self.data_form_widget, 0, 0, 2, 1)
self.xml.connect_signals(self)
self.window.show_all()
def on_cancel_button_clicked(self, widget):
self.window.destroy()
def on_ok_button_clicked(self, widget):
# send registration info to the core
if self.is_form:
form = self.data_form_widget.data_form
app.connections[self.account].register_agent(self.service,
form, True) # True is for is_form
else:
infos = self.data_form_widget.get_infos()
if 'instructions' in infos:
del infos['instructions']
if 'registered' in infos:
del infos['registered']
app.connections[self.account].register_agent(self.service, infos)
self.window.destroy()
class GroupchatConfigWindow: class GroupchatConfigWindow:
def __init__(self, account, room_jid, form=None): def __init__(self, account, room_jid, form=None):

View file

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-apply</property>
</object>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-cancel</property>
</object>
<object class="GtkWindow" id="service_registration_window">
<property name="can_focus">False</property>
<property name="border_width">6</property>
<property name="title" translatable="yes">Register to</property>
<property name="type_hint">dialog</property>
<child>
<object class="GtkBox" id="vbox12">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkGrid" id="grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="hbuttonbox5">
<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" translatable="yes">_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="image">image2</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="ok_button">
<property name="label" translatable="yes">_OK</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">False</property>
<property name="image">image1</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_ok_button_clicked" swapped="no"/>
</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>
</child>
</object>
</interface>

View file

@ -59,6 +59,7 @@ from gajim import groups
from gajim import adhoc_commands from gajim import adhoc_commands
from gajim import search_window from gajim import search_window
from gajim import gui_menu_builder from gajim import gui_menu_builder
from gajim.gtk import ServiceRegistration
from gajim.common import app from gajim.common import app
import nbxmpp import nbxmpp
@ -1355,7 +1356,7 @@ class ToplevelAgentBrowser(AgentBrowser):
return return
jid = model[iter_][0] jid = model[iter_][0]
if jid: if jid:
app.connections[self.account].request_register_agent_info(jid) ServiceRegistration(self.account, jid)
self.window.destroy(chain=True) self.window.destroy(chain=True)
def on_join_button_clicked(self, widget): def on_join_button_clicked(self, widget):

View file

@ -66,3 +66,4 @@ from gajim.gtk.bookmarks import ManageBookmarksWindow
from gajim.gtk.profile import ProfileWindow from gajim.gtk.profile import ProfileWindow
from gajim.gtk.features import FeaturesDialog from gajim.gtk.features import FeaturesDialog
from gajim.gtk.account_wizard import AccountCreationWizard from gajim.gtk.account_wizard import AccountCreationWizard
from gajim.gtk.service_registration import ServiceRegistration

View file

@ -209,7 +209,8 @@ class AddNewContactWindow(Gtk.ApplicationWindow):
model = self.protocol_jid_combobox.get_model() model = self.protocol_jid_combobox.get_model()
row = self.protocol_jid_combobox.get_active() row = self.protocol_jid_combobox.get_active()
jid = model[row][0] jid = model[row][0]
app.connections[self.account].request_register_agent_info(jid) from gajim.gtk import ServiceRegistration
ServiceRegistration(self.account, jid)
def _on_key_press(self, widget, event): def _on_key_press(self, widget, event):
if event.keyval == Gdk.KEY_Escape: if event.keyval == Gdk.KEY_Escape:

View file

@ -13,10 +13,10 @@
# along with Gajim. If not, see <http://www.gnu.org/licenses/>. # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import Gdk
from gajim.common import app from gajim.common import app
from gajim.common import helpers from gajim.common import helpers
from gajim.common.modules import dataforms
from gajim.gtk.util import get_builder from gajim.gtk.util import get_builder
from gajim.gtk.util import load_icon from gajim.gtk.util import load_icon

View file

@ -0,0 +1,198 @@
# 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 enum import IntEnum
from gi.repository import Gtk
from gajim.common import app
from gajim.common.modules import dataforms
log = logging.getLogger('gajim.gtk.registration')
class Page(IntEnum):
REQUEST = 0
FORM = 1
SENDING = 2
SUCCESS = 3
ERROR = 4
class ServiceRegistration(Gtk.Assistant):
def __init__(self, account, agent):
Gtk.Assistant.__init__(self)
self._con = app.connections[account]
self._agent = agent
self._account = account
self._data_form_widget = None
self._is_form = None
self.set_application(app.app)
self.set_resizable(True)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_default_size(500, 300)
self.get_style_context().add_class('dialog-margin')
request = RequestPage()
self.append_page(request)
self.set_page_type(request, Gtk.AssistantPageType.INTRO)
form = FormPage()
self.append_page(form)
self.set_page_type(form, Gtk.AssistantPageType.INTRO)
self.set_page_complete(form, True)
sending = SendingPage()
self.append_page(sending)
self.set_page_type(sending, Gtk.AssistantPageType.PROGRESS)
success = SuccessfulPage()
self.append_page(success)
self.set_page_type(success, Gtk.AssistantPageType.SUMMARY)
self.set_page_complete(success, True)
error = ErrorPage()
self.append_page(error)
self.set_page_type(error, Gtk.AssistantPageType.SUMMARY)
self.set_page_complete(error, True)
self.connect('prepare', self._on_page_change)
self.connect('cancel', self._on_cancel)
self.connect('close', self._on_cancel)
self.show_all()
def _on_page_change(self, assistant, page):
if self.get_current_page() == Page.REQUEST:
self._con.get_module('Register').get_register_form(
self._agent, self._on_get_success, self._on_error)
elif self.get_current_page() == Page.SENDING:
self._register()
self.commit()
pass
def _on_get_success(self, form, is_form):
log.info('Show Form page')
self._is_form = is_form
if is_form:
from gajim import dataforms_widget
dataform = dataforms.ExtendForm(node=form)
self._data_form_widget = dataforms_widget.DataFormWidget(dataform)
if self._data_form_widget.title:
self.set_title('%s - Gajim' % self._data_form_widget.title)
else:
if 'registered' in form:
self.set_title(_('Edit %s') % self._agent)
else:
self.set_title(_('Register to %s') % self._agent)
from gajim import config
self._data_form_widget = config.FakeDataForm(form)
page = self.get_nth_page(Page.FORM)
page.pack_start(self._data_form_widget, True, True, 0)
self._data_form_widget.show()
self.set_current_page(Page.FORM)
def _on_error(self, error_text):
log.info('Show Error page')
page = self.get_nth_page(Page.ERROR)
page.set_text(error_text)
self.set_current_page(Page.ERROR)
def _on_cancel(self, widget):
self.destroy()
def _register(self):
log.info('Show Sending page')
if self._is_form:
form = self._data_form_widget.data_form
else:
form = self._data_form_widget.get_infos()
if 'instructions' in form:
del form['instructions']
if 'registered' in form:
del form['registered']
self._con.get_module('Register').register_agent(
self._agent,
form,
self._is_form,
self._on_register_success,
self._on_error)
def _on_register_success(self):
log.info('Show Success page')
self.set_current_page(Page.SUCCESS)
class RequestPage(Gtk.Box):
def __init__(self):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
self.set_spacing(18)
spinner = Gtk.Spinner()
self.pack_start(spinner, True, True, 0)
spinner.start()
class SendingPage(RequestPage):
def __init__(self):
super().__init__()
class FormPage(Gtk.Box):
def __init__(self):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
class SuccessfulPage(Gtk.Box):
def __init__(self):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
self.set_spacing(12)
self.set_homogeneous(True)
icon = Gtk.Image.new_from_icon_name('object-select-symbolic',
Gtk.IconSize.DIALOG)
icon.get_style_context().add_class('success-color')
icon.set_valign(Gtk.Align.END)
label = Gtk.Label(label=_('Registration successful'))
label.get_style_context().add_class('bold16')
label.set_valign(Gtk.Align.START)
self.add(icon)
self.add(label)
class ErrorPage(Gtk.Box):
def __init__(self):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
self.set_spacing(12)
self.set_homogeneous(True)
icon = Gtk.Image.new_from_icon_name('dialog-error-symbolic',
Gtk.IconSize.DIALOG)
icon.get_style_context().add_class('error-color')
icon.set_valign(Gtk.Align.END)
self._label = Gtk.Label()
self._label.get_style_context().add_class('bold16')
self._label.set_valign(Gtk.Align.START)
self.add(icon)
self.add(self._label)
def set_text(self, text):
self._label.set_text(text)

View file

@ -580,17 +580,6 @@ class Interface:
'unsubscribed', 'gajim-unsubscribed', 'unsubscribed', 'gajim-unsubscribed',
event_type, obj.jid) event_type, obj.jid)
@staticmethod
def handle_event_register_agent_info(obj):
# ('REGISTER_AGENT_INFO', account, (agent, infos, is_form))
# info in a dataform if is_form is True
if obj.is_form or 'instructions' in obj.config:
config.ServiceRegistrationWindow(obj.agent, obj.config,
obj.conn.name, obj.is_form)
else:
ErrorDialog(_('Contact with "%s" cannot be established') % \
obj.agent, _('Check your connection or try again later.'))
def handle_event_gc_config(self, obj): def handle_event_gc_config(self, obj):
#('GC_CONFIG', account, (jid, form_node)) config is a dict #('GC_CONFIG', account, (jid, form_node)) config is a dict
account = obj.conn.name account = obj.conn.name
@ -1565,7 +1554,6 @@ class Interface:
'password-required': [self.handle_event_password_required], 'password-required': [self.handle_event_password_required],
'plain-connection': [self.handle_event_plain_connection], 'plain-connection': [self.handle_event_plain_connection],
'presence-received': [self.handle_event_presence], 'presence-received': [self.handle_event_presence],
'register-agent-info-received': [self.handle_event_register_agent_info],
'roster-info': [self.handle_event_roster_info], 'roster-info': [self.handle_event_roster_info],
'roster-item-exchange-received': \ 'roster-item-exchange-received': \
[self.handle_event_roster_item_exchange], [self.handle_event_roster_item_exchange],

View file

@ -71,6 +71,7 @@ from gajim.gtk import AddNewContactWindow
from gajim.gtk import ManagePEPServicesWindow from gajim.gtk import ManagePEPServicesWindow
from gajim.gtk import ManageBookmarksWindow from gajim.gtk import ManageBookmarksWindow
from gajim.gtk import AccountCreationWizard from gajim.gtk import AccountCreationWizard
from gajim.gtk import ServiceRegistration
from gajim.common.const import AvatarSize from gajim.common.const import AvatarSize
@ -2790,7 +2791,7 @@ class RosterWindow:
""" """
When we want to modify the agent registration When we want to modify the agent registration
""" """
app.connections[account].request_register_agent_info(contact.jid) ServiceRegistration(account, contact.jid)
def on_remove_agent(self, widget, list_): def on_remove_agent(self, widget, list_):
""" """