Skip to content
Snippets Groups Projects
Commit 1a7d930f authored by Philipp Hörist's avatar Philipp Hörist
Browse files

Add blocking list dialog

parent b0742377
No related branches found
No related tags found
No related merge requests found
......@@ -44,6 +44,7 @@ from gajim.gtk.history import HistoryWindow
from gajim.gtk.accounts import AccountsWindow
from gajim.gtk.proxies import ManageProxies
from gajim.gtk.discovery import ServiceDiscoveryWindow
from gajim.gtk.blocking import BlockingList
# General Actions
......@@ -211,6 +212,15 @@ def on_mam_preferences(action, param):
window.present()
def on_blocking_list(action, param):
account = param.get_string()
window = app.get_app_window(MamPreferences, account)
if window is None:
BlockingList(account)
else:
window.present()
def on_history_sync(action, param):
account = param.get_string()
if 'history_sync' in interface.instances[account]:
......
......@@ -469,6 +469,7 @@ class GajimApplication(Gtk.Application):
('-archive', a.on_mam_preferences, 'feature', 's'),
('-sync-history', a.on_history_sync, 'online', 's'),
('-privacylists', a.on_privacy_lists, 'feature', 's'),
('-blocking', a.on_blocking_list, 'feature', 's'),
('-send-server-message', a.on_send_server_message, 'online', 's'),
('-set-motd', a.on_set_motd, 'online', 's'),
('-update-motd', a.on_update_motd, 'online', 's'),
......@@ -517,3 +518,6 @@ class GajimApplication(Gtk.Application):
elif event.feature == nbxmpp.NS_PRIVACY:
action = '%s-privacylists' % event.account
self.lookup_action(action).set_enabled(True)
elif event.feature == nbxmpp.NS_BLOCKING:
action = '%s-blocking' % event.account
self.lookup_action(action).set_enabled(True)
......@@ -15,15 +15,26 @@
# XEP-0191: Blocking Command
import logging
from functools import wraps
import nbxmpp
from gajim.common import app
from gajim.common.nec import NetworkEvent
from gajim.common.nec import NetworkIncomingEvent
log = logging.getLogger('gajim.c.m.blocking')
def ensure_online(func):
@wraps(func)
def func_wrapper(self, *args, **kwargs):
if not app.account_is_connected(self._account):
return
return func(self, *args, **kwargs)
return func_wrapper
class Blocking:
def __init__(self, con):
self._con = con
......@@ -37,37 +48,48 @@ class Blocking:
self.supported = False
self._nbmxpp_methods = [
'block',
'unblock',
]
def __getattr__(self, key):
if key not in self._nbmxpp_methods:
raise AttributeError
if not app.account_is_connected(self._account):
log.warning('Account %s not connected, cant use %s',
self._account, key)
return
module = self._con.connection.get_module('Blocking')
return getattr(module, key)
def pass_disco(self, from_, _identities, features, _data, _node):
if nbxmpp.NS_BLOCKING not in features:
return
self.supported = True
app.nec.push_incoming_event(
NetworkEvent('feature-discovered',
account=self._account,
feature=nbxmpp.NS_BLOCKING))
log.info('Discovered blocking: %s', from_)
def get_blocking_list(self) -> None:
if not self.supported:
return
iq = nbxmpp.Iq('get', nbxmpp.NS_BLOCKING)
iq.setQuery('blocklist')
@ensure_online
def get_blocking_list(self, callback=None):
log.info('Request list')
self._con.connection.SendAndCallForResponse(
iq, self._blocking_list_received)
if callback is None:
callback = self._blocking_list_received
def _blocking_list_received(self, stanza: nbxmpp.Iq) -> None:
if not nbxmpp.isResultNode(stanza):
log.info('Error: %s', stanza.getError())
return
self._con.connection.get_module('Blocking').get_blocking_list(
callback=callback)
self.blocked = []
blocklist = stanza.getTag('blocklist', namespace=nbxmpp.NS_BLOCKING)
if blocklist is None:
log.error('No blocklist node')
def _blocking_list_received(self, result):
if result.is_error:
log.info('Error: %s', result.error)
return
for item in blocklist.getTags('item'):
self.blocked.append(item.getAttr('jid'))
log.info('Received list: %s', self.blocked)
self.blocked = result.blocking_list
app.nec.push_incoming_event(
BlockingEvent(None, conn=self._con, changed=self.blocked))
......@@ -129,35 +151,6 @@ class Blocking:
probe = nbxmpp.Presence(jid, 'probe', frm=self._con.get_own_jid())
self._con.connection.send(probe)
def block(self, contact_list):
if not self.supported:
return
iq = nbxmpp.Iq('set', nbxmpp.NS_BLOCKING)
query = iq.setQuery(name='block')
for contact in contact_list:
query.addChild(name='item', attrs={'jid': contact.jid})
log.info('Block: %s', contact.jid)
self._con.connection.SendAndCallForResponse(
iq, self._default_result_handler, {})
def unblock(self, contact_list):
if not self.supported:
return
iq = nbxmpp.Iq('set', nbxmpp.NS_BLOCKING)
query = iq.setQuery(name='unblock')
for contact in contact_list:
query.addChild(name='item', attrs={'jid': contact.jid})
log.info('Unblock: %s', contact.jid)
self._con.connection.SendAndCallForResponse(
iq, self._default_result_handler, {})
@staticmethod
def _default_result_handler(_con, stanza):
if not nbxmpp.isResultNode(stanza):
log.warning('Operation failed: %s', stanza.getError())
class BlockingEvent(NetworkIncomingEvent):
name = 'blocking'
......
......@@ -304,7 +304,8 @@ class PrivacyLists:
def block_contacts(self, contact_list, message):
if not self.supported:
self._con.get_module('Blocking').block(contact_list)
jid_list = [contact.jid for contact in contact_list]
self._con.get_module('Blocking').block(jid_list)
return
if self.default_list is None:
......@@ -350,7 +351,8 @@ class PrivacyLists:
def unblock_contacts(self, contact_list):
if not self.supported:
self._con.get_module('Blocking').unblock(contact_list)
jid_list = [contact.jid for contact in contact_list]
self._con.get_module('Blocking').unblock(jid_list)
return
new_blocked_list = []
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<object class="GtkListStore" id="blocking_store">
<columns>
<!-- column-name jid -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkGrid" id="blocking_grid">
<property name="width_request">400</property>
<property name="height_request">300</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">18</property>
<property name="margin_right">18</property>
<property name="margin_top">18</property>
<property name="margin_bottom">18</property>
<property name="row_spacing">5</property>
<property name="column_spacing">10</property>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="spacing">5</property>
<property name="layout_style">start</property>
<child>
<object class="GtkButton" id="add_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="_on_add" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-add-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
<property name="non_homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkButton" id="remove_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="_on_remove" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-remove-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="non_homogeneous">True</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save_button">
<property name="label">Save</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">end</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="_on_save" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkOverlay" id="overlay">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow">
<property name="height_request">150</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="block_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">blocking_store</property>
<property name="search_column">0</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection2"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="treeviewcolumn1">
<property name="title" translatable="yes">Jabber ID</property>
<property name="expand">True</property>
<property name="clickable">True</property>
<property name="sort_indicator">True</property>
<property name="sort_column_id">0</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext3">
<property name="editable">True</property>
<property name="placeholder_text">user@example.org</property>
<signal name="edited" handler="_jid_edited" swapped="no"/>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
</object>
</interface>
# 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 gi.repository import Gdk
from gajim.common import app
from gajim.common.i18n import _
from gajim.gtk.util import get_builder
from gajim.gtk.dialogs import HigDialog
log = logging.getLogger('gajim.gtk.blocking_list')
class BlockingList(Gtk.ApplicationWindow):
def __init__(self, account):
Gtk.ApplicationWindow.__init__(self)
self.set_application(app.app)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_show_menubar(False)
self.set_title(_('Blocking List for %s') % account)
self.connect('key-press-event', self._on_key_press)
self.account = account
self._con = app.connections[account]
self._prev_blocked_jids = set()
self._await_results = 2
self._received_errors = False
self._ui = get_builder('blocking_list.ui')
self.add(self._ui.blocking_grid)
self._spinner = Gtk.Spinner()
self._ui.overlay.add_overlay(self._spinner)
self._set_grid_state(False)
self._ui.connect_signals(self)
self.show_all()
self._activate_spinner()
self._con.get_module('Blocking').get_blocking_list(
callback=self._on_blocking_list_received)
def _reset_after_error(self):
self._received_errors = False
self._await_results = 2
self._disable_spinner()
self._set_grid_state(True)
def _show_error(self, error):
dialog = HigDialog(
self, Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
_('Error!'),
error)
dialog.popup()
def _on_blocking_list_received(self, result):
self._disable_spinner()
self._set_grid_state(not result.is_error)
if result.is_error:
self._show_error(result.error)
else:
self._prev_blocked_jids = set(result.blocking_list)
self._ui.blocking_store.clear()
for item in result.blocking_list:
self._ui.blocking_store.append((item,))
def _on_save_result(self, result):
self._await_results -= 1
if result.is_error and not self._received_errors:
self._show_error(result.error)
self._received_errors = True
if not self._await_results:
if self._received_errors:
self._reset_after_error()
else:
self.destroy()
def _set_grid_state(self, state):
self._ui.blocking_grid.set_sensitive(state)
def _jid_edited(self, _renderer, path, new_text):
iter_ = self._ui.blocking_store.get_iter(path)
self._ui.blocking_store.set_value(iter_, 0, new_text)
def _on_add(self, _button):
self._ui.blocking_store.append([''])
def _on_remove(self, _button):
mod, paths = self._ui.block_view.get_selection().get_selected_rows()
for path in paths:
iter_ = mod.get_iter(path)
self._ui.blocking_store.remove(iter_)
def _on_save(self, _button):
self._activate_spinner()
self._set_grid_state(False)
blocked_jids = set()
for item in self._ui.blocking_store:
blocked_jids.add(item[0].lower())
unblock_jids = self._prev_blocked_jids - blocked_jids
if unblock_jids:
self._con.get_module('Blocking').unblock(
unblock_jids, callback=self._on_save_result)
else:
self._await_results -= 1
block_jids = blocked_jids - self._prev_blocked_jids
if block_jids:
self._con.get_module('Blocking').block(
block_jids, callback=self._on_save_result)
else:
self._await_results -= 1
if not self._await_results:
# No changes
self.destroy()
def _activate_spinner(self):
self._spinner.show()
self._spinner.start()
def _disable_spinner(self):
self._spinner.hide()
self._spinner.stop()
def _on_key_press(self, _widget, event):
if event.keyval == Gdk.KEY_Escape:
self.destroy()
......@@ -788,6 +788,7 @@ def get_account_menu(account):
('-start-single-chat', _('Send Single Message…')),
(_('Advanced'), [
('-archive', _('Archiving Preferences')),
('-blocking', _('Blocking List')),
('-sync-history', _('Synchronise History')),
('-privacylists', _('Privacy Lists')),
('-server-info', _('Server Info')),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment