Philipp Hörist pushed to branch gajim_1.2 at gajim / gajim
Commits: 1448b59b by wurstsalat at 2020-07-07T21:59:30+02:00 ChatControl: Remove account string from banner
- - - - - 216eae42 by wurstsalat at 2020-07-07T21:59:30+02:00 ChatControl: Add menu item for blocking/reporting
- - - - - a0810652 by wurstsalat at 2020-07-07T21:59:30+02:00 Roster: Adapt strings in blocking dialog
- - - - - aad99763 by lovetox at 2020-07-07T21:59:30+02:00 Roster: Remove blocking whole group feature
Multiselecting contacts should be efficient enough for such rare action
- - - - - e6ac4e4b by lovetox at 2020-07-07T21:59:30+02:00 Roster: Don't depend on Privacy lists for multiple contact block
- - - - - 2362db29 by lovetox at 2020-07-07T21:59:30+02:00 Remove unused methods
- - - - - 2a3927f7 by lovetox at 2020-07-07T21:59:30+02:00 Remove privacy lists support
Fixes #10161
- - - - - d16eaa93 by Daniel Brötzmann at 2020-07-07T21:59:30+02:00 Roster: Add spam reporting
- - - - - ad93a76d by lovetox at 2020-07-07T21:59:30+02:00 Groupchat: Handle invites consistently
- Always print a status message - Display errors on invite
Fixes #10171
- - - - - e5d7704d by wurstsalat at 2020-07-07T21:59:30+02:00 DOAP: Remove XEP-0016
- - - - - 8801e04a by wurstsalat at 2020-07-07T21:59:30+02:00 DOAP: Add XEP-0377
- - - - - 0e405e2a by lovetox at 2020-07-07T21:59:30+02:00 Groupchat: Display recent history from the database
- - - - - 391256de by wurstsalat at 2020-07-07T21:59:30+02:00 Preferences: Add preview switch and placeholder
- - - - -
22 changed files:
- data/gajim.doap - gajim/app_actions.py - gajim/application.py - gajim/chat_control.py - gajim/chat_control_base.py - gajim/command_system/implementation/standard.py - gajim/common/app.py - gajim/common/helpers.py - gajim/common/logger.py - gajim/common/modules/__init__.py - gajim/common/modules/discovery.py - gajim/common/modules/muc.py - − gajim/common/modules/privacylists.py - gajim/data/gui/preferences_window.ui - gajim/dialog_messages.py - gajim/groupchat_control.py - gajim/gtk/const.py - gajim/gtk/preferences.py - − gajim/gtk/privacy_list.py - gajim/gtk/server_info.py - gajim/gui_menu_builder.py - gajim/roster_window.py
Changes:
===================================== data/gajim.doap ===================================== @@ -46,13 +46,6 @@ xmpp:version2.9</xmpp:version> </xmpp:SupportedXep> </implements> - <implements> - xmpp:SupportedXep - <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0016.html" /> - xmpp:statuscomplete</xmpp:status> - xmpp:version1.6</xmpp:version> - </xmpp:SupportedXep> - </implements> <implements> xmpp:SupportedXep <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0027.html" /> @@ -546,6 +539,14 @@ xmpp:noteOpenPGP plugin.</xmpp:note> </xmpp:SupportedXep> </implements> + <implements> + xmpp:SupportedXep + <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0377.html" /> + xmpp:statuspartial</xmpp:status> + xmpp:version0.2</xmpp:version> + xmpp:noteBasic Spam/Abuse reporting via XEP-0191</xmpp:note> + </xmpp:SupportedXep> + </implements> <implements> xmpp:SupportedXep <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0380.html" />
===================================== gajim/app_actions.py ===================================== @@ -26,7 +26,6 @@ from gajim import dialogs from gajim.gtk.dialogs import ShortcutsWindow from gajim.gtk.single_message import SingleMessageWindow from gajim.gtk.about import AboutDialog -from gajim.gtk.privacy_list import PrivacyListsWindow from gajim.gtk.history import HistoryWindow from gajim.gtk.discovery import ServiceDiscoveryWindow from gajim.gtk.util import open_window @@ -171,15 +170,6 @@ def on_history_sync(_action, param): parent=interface.roster.window)
-def on_privacy_lists(_action, param): - account = param.get_string() - if 'privacy_lists' in interface.instances[account]: - interface.instances[account]['privacy_lists'].window.present() - else: - interface.instances[account]['privacy_lists'] = \ - PrivacyListsWindow(account) - - def on_server_info(_action, param): account = param.get_string() open_window('ServerInfo', account=account)
===================================== gajim/application.py ===================================== @@ -473,7 +473,6 @@ class GajimApplication(Gtk.Application): ('-archive', a.on_mam_preferences, 'feature', 's'), ('-pep-config', a.on_pep_config, 'online', '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'), @@ -571,9 +570,6 @@ class GajimApplication(Gtk.Application): elif event.feature == Namespace.MAM_2: action = '%s-archive' % event.account self.lookup_action(action).set_enabled(True) - elif event.feature == Namespace.PRIVACY: - action = '%s-privacylists' % event.account - self.lookup_action(action).set_enabled(True) elif event.feature == Namespace.BLOCKING: action = '%s-blocking' % event.account self.lookup_action(action).set_enabled(True)
===================================== gajim/chat_control.py ===================================== @@ -251,8 +251,9 @@ class ChatControl(ChatControlBase): actions = [ ('invite-contacts-', self._on_invite_contacts), ('add-to-roster-', self._on_add_to_roster), + ('block-contact-', self._on_block_contact), ('information-', self._on_information), - ] + ]
for action in actions: action_name, func = action @@ -300,6 +301,11 @@ class ChatControl(ChatControlBase): win.lookup_action( 'add-to-roster-' + self.control_id).set_enabled(False)
+ # Block contact + win.lookup_action( + 'block-contact-' + self.control_id).set_enabled( + online and con.get_module('Blocking').supported) + # Audio win.lookup_action('toggle-audio-' + self.control_id).set_enabled( online and self.jingle['audio'].available) @@ -376,6 +382,33 @@ class ChatControl(ChatControlBase): def _on_add_to_roster(self, _action, _param): AddNewContactWindow(self.account, self.contact.jid)
+ def _on_block_contact(self, _action, _param): + def _block_contact(report=None): + con = app.connections[self.account] + con.get_module('Blocking').block([self.contact.jid], report=report) + + self.parent_win.remove_tab(self, None, force=True) + if _('Not in contact list') in self.contact.get_shown_groups(): + app.interface.roster.remove_contact( + self.contact.jid, self.account, force=True, backend=True) + return + app.interface.roster.draw_contact(self.contact.jid, self.account) + + NewConfirmationDialog( + _('Block Contact'), + _('Really block this contact?'), + _('You will appear offline for this contact and you will ' + 'not receive further messages.'), + [DialogButton.make('Cancel'), + DialogButton.make('OK', + text=_('_Report Spam'), + callback=_block_contact, + kwargs={'report': 'spam'}), + DialogButton.make('Remove', + text=_('_Block'), + callback=_block_contact)], + modal=False).show() + def _on_information(self, _action, _param): app.interface.roster.on_info(None, self.contact, self.account)
@@ -802,37 +835,15 @@ class ChatControl(ChatControlBase): name, jid """ contact = self.contact - jid = contact.jid - name = contact.get_shown_name() if self.resource: name += '/' + self.resource if self._type.is_privatechat: - name = i18n.direction_mark + _( + name = i18n.direction_mark + _( '%(nickname)s from group chat %(room_name)s') % \ {'nickname': name, 'room_name': self.room_name} name = i18n.direction_mark + GLib.markup_escape_text(name)
- # We know our contacts nick, but if another contact has the same nick - # in another account we need to also display the account. - # except if we are talking to two different resources of the - # same contact - acct_info = '' - for account in app.contacts.get_accounts(): - if account == self.account: - continue - if acct_info: # We already found a contact with same nick - break - for jid in app.contacts.get_jid_list(account): - other_contact_ = \ - app.contacts.get_first_contact_from_jid(account, jid) - if other_contact_.get_shown_name() == \ - self.contact.get_shown_name(): - acct_info = i18n.direction_mark + ' (%s)' % \ - GLib.markup_escape_text( - app.get_account_label(self.account)) - break - status = contact.status if status is not None: status_reduced = helpers.reduce_chars_newlines(status, max_lines=1) @@ -849,17 +860,12 @@ class ChatControl(ChatControlBase): if app.config.get('show_chatstate_in_banner'): chatstate = helpers.get_uf_chatstate(cs)
- label_text = '<span>%s</span><span size="x-small" weight="light">%s %s</span>' \ - % (name, acct_info, chatstate) - if acct_info: - acct_info = i18n.direction_mark + ' ' + acct_info - label_tooltip = '%s%s %s' % (name, acct_info, chatstate) + label_text = '<span>%s</span><span size="x-small" weight="light"> %s</span>' % \ + (name, chatstate) + label_tooltip = '%s %s' % (name, chatstate) else: - label_text = '<span>%s</span><span size="x-small" weight="light">%s</span>' % \ - (name, acct_info) - if acct_info: - acct_info = i18n.direction_mark + ' ' + acct_info - label_tooltip = '%s%s' % (name, acct_info) + label_text = '<span>%s</span>' % name + label_tooltip = name
if status_escaped: status_text = make_href_markup(status_escaped)
===================================== gajim/chat_control_base.py ===================================== @@ -1071,9 +1071,9 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper): else: self.received_history_pos = pos
- def add_info_message(self, text): + def add_info_message(self, text, message_id=None): self.conv_textview.print_conversation_line( - text, 'info', '', None, graphics=False) + text, 'info', '', None, message_id=message_id, graphics=False)
def add_status_message(self, text): self.conv_textview.print_conversation_line(
===================================== gajim/command_system/implementation/standard.py ===================================== @@ -308,10 +308,9 @@ class StandardGroupChatCommands(CommandContainer): @command(raw=True, empty=True) @doc(_("Invite a user to a group chat for a reason")) def invite(self, jid, reason): - self.connection.get_module('MUC').invite(self.room_jid, jid, reason) - return _("Invited %(jid)s to %(room_jid)s") % { - 'jid': jid, - 'room_jid': self.room_jid} + control = app.get_groupchat_control(self.account, self.room_jid) + if control is not None: + control.invite(jid)
@command(raw=True, empty=True) @doc(_("Join a group chat given by an XMPP Address"))
===================================== gajim/common/app.py ===================================== @@ -644,3 +644,12 @@ def get_stored_bob_data(algo_hash: str) -> Optional[bytes]: data = file.read() return data return None + +def get_groupchat_control(account, jid): + control = app.interface.msg_win_mgr.get_gc_control(jid, account) + if control is not None: + return control + try: + return app.interface.minimized_controls[account][jid] + except Exception: + return None
===================================== gajim/common/helpers.py ===================================== @@ -833,14 +833,7 @@ def get_optional_features(account):
def jid_is_blocked(account, jid): con = app.connections[account] - return (jid in con.get_module('Blocking').blocked or - jid in con.get_module('PrivacyLists').blocked_contacts or - con.get_module('PrivacyLists').blocked_all) - -def group_is_blocked(account, group): - con = app.connections[account] - return (group in con.get_module('PrivacyLists').blocked_groups or - con.get_module('PrivacyLists').blocked_all) + return jid in con.get_module('Blocking').blocked
def get_subscription_request_msg(account=None): s = app.config.get_per('accounts', account, 'subscription_request_msg')
===================================== gajim/common/logger.py ===================================== @@ -526,9 +526,9 @@ class Logger: return [user['jid'] for user in family] return [jid]
- def get_account_id(self, account): + def get_account_id(self, account, type_=JIDConstant.NORMAL_TYPE): jid = app.get_jid_from_account(account) - return self.get_jid_id(jid, type_=JIDConstant.NORMAL_TYPE) + return self.get_jid_id(jid, type_=type_)
@timeit def get_jid_id(self, jid, kind=None, type_=None): @@ -755,6 +755,26 @@ class Logger: all_messages.append((result, shown)) return all_messages
+ def load_groupchat_messages(self, account, jid): + account_id = self.get_account_id(account, type_=JIDConstant.ROOM_TYPE) + + sql = ''' + SELECT time, contact_name, message, additional_data, message_id + FROM logs NATURAL JOIN jids WHERE jid = ? + AND account_id = ? AND kind = ? + ORDER BY time DESC, log_line_id DESC LIMIT ?''' + + try: + messages = self._con.execute( + sql, (jid, account_id, KindConstant.GC_MSG, 50)).fetchall() + except sqlite.DatabaseError: + self.dispatch('DB_ERROR', + exceptions.DatabaseMalformed(self._log_db_path)) + return [] + + messages.reverse() + return messages + @timeit def get_last_conversation_lines(self, account, jid, pending): """
===================================== gajim/common/modules/__init__.py ===================================== @@ -56,7 +56,6 @@ MODULES = [ 'pep', 'ping', 'presence', - 'privacylists', 'pubsub', 'receipts', 'register', @@ -99,11 +98,6 @@ class ModuleMock: # Blocking self.blocked = [] # type: List[Any]
- # Privacy Lists - self.blocked_contacts = [] # type: List[Any] - self.blocked_groups = [] # type: List[Any] - self.blocked_all = False - # Delimiter self.delimiter = '::'
===================================== gajim/common/modules/discovery.py ===================================== @@ -137,7 +137,6 @@ class Discovery(BaseModule): self._con.get_module('Blocking').pass_disco(result) self._con.get_module('VCardTemp').pass_disco(result) self._con.get_module('Carbons').pass_disco(result) - self._con.get_module('PrivacyLists').pass_disco(result) self._con.get_module('HTTPUpload').pass_disco(result)
if Namespace.REGISTER in result.features:
===================================== gajim/common/modules/muc.py ===================================== @@ -777,7 +777,8 @@ class MUC(BaseModule):
password = app.gc_passwords.get(room, None) self._log.info('Invite %s to %s', to, room) - self._nbxmpp('MUC').invite(room, to, reason, password, continue_, type_) + return self._nbxmpp('MUC').invite(room, to, reason, password, + continue_, type_)
@event_filter(['account']) def _on_account_disconnected(self, _event):
===================================== gajim/common/modules/privacylists.py deleted ===================================== @@ -1,436 +0,0 @@ -# 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/. - -# XEP-0016: Privacy Lists - -import nbxmpp -from nbxmpp.namespaces import Namespace -from nbxmpp.structs import StanzaHandler - -from gajim.common import app -from gajim.common import helpers -from gajim.common.nec import NetworkEvent -from gajim.common.nec import NetworkIncomingEvent -from gajim.common.modules.base import BaseModule -from gajim.common.connection_handlers_events import InformationEvent - - -class PrivacyLists(BaseModule): - def __init__(self, con): - BaseModule.__init__(self, con) - - self.default_list = None - self.active_list = None - self.blocked_contacts = [] - self.blocked_groups = [] - self.blocked_list = [] - self.blocked_all = False - - self.handlers = [ - StanzaHandler(name='iq', - callback=self._list_push_received, - typ='set', - ns=Namespace.PRIVACY), - ] - - self.supported = False - - def pass_disco(self, info): - if Namespace.PRIVACY not in info.features: - return - - self.supported = True - self._log.info('Discovered XEP-0016: Privacy Lists: %s', info.jid) - - app.nec.push_incoming_event( - NetworkEvent('feature-discovered', - account=self._account, - feature=Namespace.PRIVACY)) - - def _list_push_received(self, _con, stanza, _properties): - result = stanza.buildReply('result') - result.delChild(result.getTag('query')) - self._con.connection.send(result) - - for list_ in stanza.getQueryPayload(): - if list_.getName() == 'list': - name = list_.getAttr('name') - self._log.info('Received Push: %s', name) - self.get_privacy_list(name) - - raise nbxmpp.NodeProcessed - - def get_privacy_lists(self, callback=None): - self._log.info('Request lists') - iq = nbxmpp.Iq('get', Namespace.PRIVACY) - self._con.connection.SendAndCallForResponse( - iq, self._privacy_lists_received, {'callback': callback}) - - def _privacy_lists_received(self, _nbxmpp_client, stanza, callback): - lists = [] - new_default = None - result = nbxmpp.isResultNode(stanza) - if not result: - self._log.warning('List not available: %s', stanza.getError()) - else: - for list_ in stanza.getQueryPayload(): - name = list_.getAttr('name') - if list_.getName() == 'active': - self.active_list = name - elif list_.getName() == 'default': - new_default = name - else: - lists.append(name) - - self._log.info('Received lists: %s', lists) - - # Download default list if we don’t have it - if self.default_list != new_default: - self.default_list = new_default - if new_default is not None: - self._log.info('Found new default list: %s', new_default) - self.get_privacy_list(new_default) - - if callback: - callback(result) - else: - app.nec.push_incoming_event( - PrivacyListsReceivedEvent(None, - conn=self._con, - active_list=self.active_list, - default_list=self.default_list, - lists=lists)) - - def get_privacy_list(self, name): - self._log.info('Request list: %s', name) - list_ = nbxmpp.Node('list', {'name': name}) - iq = nbxmpp.Iq('get', Namespace.PRIVACY, payload=[list_]) - self._con.connection.SendAndCallForResponse( - iq, self._privacy_list_received) - - def _privacy_list_received(self, _nbxmpp_client, stanza): - if not nbxmpp.isResultNode(stanza): - self._log.warning('List not available: %s', stanza.getError()) - return - - rules = [] - list_ = stanza.getQueryPayload()[0] - name = list_.getAttr('name') - - for child in list_.getChildren(): - - item = child.getAttrs() - - children = [] - for scnd_child in child.getChildren(): - children.append(scnd_child.getName()) - - item['child'] = children - if len(item) not in (3, 5): - self._log.warning('Wrong count of attrs: %s', stanza) - continue - rules.append(item) - - self._log.info('Received list: %s', name) - - if name == self.default_list: - self._default_list_received(rules) - - app.nec.push_incoming_event(PrivacyListReceivedEvent( - None, conn=self._con, list_name=name, rules=rules)) - - def del_privacy_list(self, name): - self._log.info('Remove list: %s', name) - - def _del_privacy_list_result(_nbxmpp_client, stanza): - if not nbxmpp.isResultNode(stanza): - self._log.warning('List deletion failed: %s', stanza.getError()) - app.nec.push_incoming_event(InformationEvent( - None, dialog_name='privacy-list-error', args=name)) - else: - app.nec.push_incoming_event(PrivacyListRemovedEvent( - None, conn=self._con, list_name=name)) - - node = nbxmpp.Node('list', {'name': name}) - iq = nbxmpp.Iq('set', Namespace.PRIVACY, payload=[node]) - self._con.connection.SendAndCallForResponse( - iq, _del_privacy_list_result) - - def set_privacy_list(self, name, rules): - node = nbxmpp.Node('list', {'name': name}) - iq = nbxmpp.Iq('set', Namespace.PRIVACY, payload=[node]) - for item in rules: - children = item.get('child', []) - for child in children: - node.setTag(child) - item.pop('child', None) - node.setTag('item', item) - self._log.info('Update list: %s %s', name, rules) - self._con.connection.SendAndCallForResponse( - iq, self._default_result_handler, {}) - - def _default_list_received(self, rules): - roster = app.interface.roster - - for rule in rules: - if rule['action'] == 'allow': - if 'type' not in rule: - self.blocked_all = False - - elif rule['type'] == 'jid': - if rule['value'] in self.blocked_contacts: - self.blocked_contacts.remove(rule['value']) - - elif rule['type'] == 'group': - if rule['value'] in self.blocked_groups: - self.blocked_groups.remove(rule['value']) - - elif rule['action'] == 'deny': - if 'type' not in rule: - self.blocked_all = True - - elif rule['type'] == 'jid': - if rule['value'] not in self.blocked_contacts: - self.blocked_contacts.append(rule['value']) - - elif rule['type'] == 'group': - if rule['value'] not in self.blocked_groups: - self.blocked_groups.append(rule['value']) - - self.blocked_list.append(rule) - - if 'type' in rule: - if rule['type'] == 'jid': - roster.draw_contact(rule['value'], self._account) - if rule['type'] == 'group': - roster.draw_group(rule['value'], self._account) - - def set_active_list(self, name=None): - self._log.info('Set active list: %s', name) - attr = {} - if name: - attr['name'] = name - node = nbxmpp.Node('active', attr) - iq = nbxmpp.Iq('set', Namespace.PRIVACY, payload=[node]) - self._con.connection.SendAndCallForResponse( - iq, self._default_result_handler, {}) - - def set_default_list(self, name=None): - self._log.info('Set default list: %s', name) - attr = {} - if name: - attr['name'] = name - node = nbxmpp.Node('default', attr) - iq = nbxmpp.Iq('set', Namespace.PRIVACY, payload=[node]) - self._con.connection.SendAndCallForResponse( - iq, self._default_result_handler, {}) - - def _default_result_handler(self, _nbxmpp_client, stanza): - if not nbxmpp.isResultNode(stanza): - self._log.warning('Operation failed: %s', stanza.getError()) - - def _get_max_blocked_list_order(self): - max_order = 0 - for rule in self.blocked_list: - order = int(rule['order']) - if order > max_order: - max_order = order - return max_order - - def block_gc_contact(self, jid): - if jid in self.blocked_contacts: - return - self._log.info('Block GC contact: %s', jid) - - if self.default_list is None: - self.default_list = 'block' - - max_order = self._get_max_blocked_list_order() - new_rule = {'order': str(max_order + 1), - 'type': 'jid', - 'action': 'deny', - 'value': jid, - 'child': ['message', 'iq', 'presence-out']} - self.blocked_list.append(new_rule) - self.blocked_contacts.append(jid) - self.set_privacy_list(self.default_list, self.blocked_list) - if len(self.blocked_list) == 1: - self.set_default_list(self.default_list) - - def block_contacts(self, contact_list, _message): - if not self.supported: - jid_list = [contact.jid for contact in contact_list] - self._con.get_module('Blocking').block(jid_list) - return - - if self.default_list is None: - self.default_list = 'block' - for contact in contact_list: - self._log.info('Block contacts: %s', contact.jid) - contact.show = 'offline' - self._con.get_module('Presence').send_presence(contact.jid, - typ='unavailable') - max_order = self._get_max_blocked_list_order() - new_rule = {'order': str(max_order + 1), - 'type': 'jid', - 'action': 'deny', - 'value': contact.jid} - self.blocked_list.append(new_rule) - self.blocked_contacts.append(contact.jid) - self.set_privacy_list(self.default_list, self.blocked_list) - if len(self.blocked_list) == 1: - self.set_default_list(self.default_list) - - def unblock_gc_contact(self, jid): - new_blocked_list = [] - # needed for draw_contact: - if jid not in self.blocked_contacts: - return - - self.blocked_contacts.remove(jid) - - self._log.info('Unblock GC contact: %s', jid) - for rule in self.blocked_list: - if (rule['action'] != 'deny' or - rule['type'] != 'jid' or - rule['value'] != jid): - new_blocked_list.append(rule) - - if not new_blocked_list: - self.blocked_list = [] - self.blocked_contacts = [] - self.blocked_groups = [] - self.set_default_list(None) - self.del_privacy_list(self.default_list) - else: - self.set_privacy_list(self.default_list, new_blocked_list) - - def unblock_contacts(self, contact_list): - if not self.supported: - jid_list = [contact.jid for contact in contact_list] - self._con.get_module('Blocking').unblock(jid_list) - return - - new_blocked_list = [] - to_unblock = [] - for contact in contact_list: - self._log.info('Unblock contacts: %s', contact.jid) - to_unblock.append(contact.jid) - if contact.jid in self.blocked_contacts: - self.blocked_contacts.remove(contact.jid) - for rule in self.blocked_list: - if (rule['action'] != 'deny' or - rule['type'] != 'jid' or - rule['value'] not in to_unblock): - new_blocked_list.append(rule) - - if not new_blocked_list: - self.blocked_list = [] - self.blocked_contacts = [] - self.blocked_groups = [] - self.set_default_list(None) - self.del_privacy_list(self.default_list) - else: - self.set_privacy_list(self.default_list, new_blocked_list) - if not app.interface.roster.regroup: - show = self._con.status - else: # accounts merged - show = helpers.get_global_show() - - for contact in contact_list: - self._con.get_module('Presence').send_presence( - contact.jid, show=show, status=self._con.status_message) - self._presence_probe(contact.jid) - - def block_group(self, group, contact_list, _message): - if not self.supported: - return - if group in self.blocked_groups: - return - self.blocked_groups.append(group) - - self._log.info('Block group: %s', group) - - if self.default_list is None: - self.default_list = 'block' - - for contact in contact_list: - self._con.get_module('Presence').send_presence( - contact.jid, typ='unavailable') - - max_order = self._get_max_blocked_list_order() - new_rule = {'order': str(max_order + 1), - 'type': 'group', - 'action': 'deny', - 'value': group} - - self.blocked_list.append(new_rule) - self.set_privacy_list(self.default_list, self.blocked_list) - if len(self.blocked_list) == 1: - self.set_default_list(self.default_list) - - def unblock_group(self, group, contact_list): - if not self.supported: - return - - if group not in self.blocked_groups: - return - self.blocked_groups.remove(group) - - self._log.info('Unblock group: %s', group) - new_blocked_list = [] - for rule in self.blocked_list: - if (rule['action'] != 'deny' or - rule['type'] != 'group' or - rule['value'] != group): - new_blocked_list.append(rule) - - if not new_blocked_list: - self.blocked_list = [] - self.blocked_contacts = [] - self.blocked_groups = [] - self.set_default_list('') - self.del_privacy_list(self.default_list) - else: - self.set_privacy_list(self.default_list, new_blocked_list) - if not app.interface.roster.regroup: - show = self._con.status - else: # accounts merged - show = helpers.get_global_show() - - for contact in contact_list: - self._con.get_module('Presence').send_presence( - contact.jid, show=show, status=self._con.status_message) - - def _presence_probe(self, jid): - self._log.info('Presence probe: %s', jid) - # Send a presence Probe to get the current Status - probe = nbxmpp.Presence(jid, 'probe', frm=self._con.get_own_jid()) - self._con.connection.send(probe) - - -class PrivacyListsReceivedEvent(NetworkIncomingEvent): - name = 'privacy-lists-received' - - -class PrivacyListReceivedEvent(NetworkIncomingEvent): - name = 'privacy-list-received' - - -class PrivacyListRemovedEvent(NetworkIncomingEvent): - name = 'privacy-list-removed' - - -def get_instance(*args, **kwargs): - return PrivacyLists(*args, **kwargs), 'PrivacyLists'
===================================== gajim/data/gui/preferences_window.ui ===================================== @@ -2181,312 +2181,422 @@ $T will be replaced by auto-not-available timeout.</property> </packing> </child> <child> - <object class="GtkBox" id="audio_video_tab"> + <object class="GtkScrolledWindow"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="border_width">18</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> + <property name="can_focus">True</property> <child> - <object class="GtkGrid"> + <object class="GtkViewport"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="halign">start</property> - <property name="row_spacing">6</property> - <property name="column_spacing">12</property> - <child> - <object class="GtkLabel"> - <property name="width_request">-1</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">end</property> - <property name="label" translatable="yes">A_udio output device</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">audio_output_combobox</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">2</property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="audio_output_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <signal name="changed" handler="on_audio_output_combobox_changed" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">2</property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="audio_input_combobox"> - <property name="width_request">200</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <signal name="changed" handler="on_audio_input_combobox_changed" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">1</property> - </packing> - </child> - <child> - <object class="GtkLabel"> - <property name="width_request">-1</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">end</property> - <property name="label" translatable="yes">_Audio input device</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">audio_input_combobox</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> - </packing> - </child> - <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">Audio</property> - <property name="use_markup">True</property> - <style> - <class name="bold"/> - <class name="margin-top6"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">0</property> - </packing> - </child> - <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">Video</property> - <property name="use_markup">True</property> - <style> - <class name="bold"/> - <class name="margin-top6"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">3</property> - </packing> - </child> <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">end</property> - <property name="label" translatable="yes">_Video input device</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">video_input_combobox</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">4</property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="video_input_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <signal name="changed" handler="on_video_input_combobox_changed" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">4</property> - </packing> - </child> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">end</property> - <property name="label" translatable="yes">Video output</property> - <property name="mnemonic_widget">selected_video_output</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">5</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="selected_video_output"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">start</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">5</property> - </packing> - </child> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">end</property> - <property name="margin_top">12</property> - <property name="label" translatable="yes">Video _framerate</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">video_framerate_combobox</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">6</property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="video_framerate_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="margin_top">12</property> - <signal name="changed" handler="on_video_framerate_combobox_changed" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">6</property> - </packing> - </child> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="halign">end</property> - <property name="label" translatable="yes">Video si_ze</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">video_size_combobox</property> - <style> - <class name="dim-label"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">7</property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="video_size_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <signal name="changed" handler="on_video_size_combobox_changed" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">7</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="video_see_self_checkbutton"> - <property name="label" translatable="yes">Vi_ew own video source</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="draw_indicator">True</property> - <signal name="toggled" handler="on_video_see_self_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">8</property> - </packing> - </child> - <child> - <object class="GtkLabel"> + <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="halign">start</property> - <property name="label" translatable="yes">Connection</property> - <property name="use_markup">True</property> - <style> - <class name="bold"/> - <class name="margin-top6"/> - </style> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">9</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="stun_checkbutton"> - <property name="label" translatable="yes">STU_N server</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="halign">end</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_stun_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">10</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="stun_server_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="tooltip_text" translatable="yes">STUN server hostname. If no hostname was given, Gajim will try + <property name="orientation">vertical</property> + <child> + <object class="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="border_width">18</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel"> + <property name="width_request">-1</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="label" translatable="yes">A_udio output device</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">audio_output_combobox</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="audio_output_combobox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="changed" handler="on_audio_output_combobox_changed" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="audio_input_combobox"> + <property name="width_request">200</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="changed" handler="on_audio_input_combobox_changed" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="width_request">-1</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="label" translatable="yes">_Audio input device</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">audio_input_combobox</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <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">Audio</property> + <property name="use_markup">True</property> + <style> + <class name="bold"/> + <class name="margin-top6"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <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">Video</property> + <property name="use_markup">True</property> + <style> + <class name="bold"/> + <class name="margin-top6"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="label" translatable="yes">_Video input device</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">video_input_combobox</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="video_input_combobox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="changed" handler="on_video_input_combobox_changed" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="stun_checkbutton"> + <property name="label" translatable="yes">STU_N server</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="halign">end</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_stun_checkbutton_toggled" swapped="no"/> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">11</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="stun_server_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip_text" translatable="yes">STUN server hostname. If no hostname was given, Gajim will try to discover one from the server. (Example: stun.iptel.org)</property> - <property name="invisible_char">●</property> - <signal name="changed" handler="stun_server_entry_changed" swapped="no"/> + <property name="invisible_char">●</property> + <signal name="changed" handler="stun_server_entry_changed" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">11</property> + </packing> + </child> + <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">Connection</property> + <property name="use_markup">True</property> + <style> + <class name="bold"/> + <class name="margin-top6"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">10</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="video_see_self_checkbutton"> + <property name="label" translatable="yes">Vi_ew own video source</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="draw_indicator">True</property> + <signal name="toggled" handler="on_video_see_self_checkbutton_toggled" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">9</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="video_size_combobox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="changed" handler="on_video_size_combobox_changed" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">8</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="label" translatable="yes">Video si_ze</property> + <property name="use_underline">True</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">8</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_top">12</property> + <property name="label" translatable="yes">Video _framerate</property> + <property name="use_underline">True</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">7</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="video_framerate_combobox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">12</property> + <signal name="changed" handler="on_video_framerate_combobox_changed" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">7</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="label" translatable="yes">Video output</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">6</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="selected_video_output"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">6</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="live_preview_checkbutton"> + <property name="label" translatable="yes">Show live _preview</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="draw_indicator">True</property> + <signal name="toggled" handler="_on_live_preview_toggled" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">5</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <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="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkSeparator"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">12</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="av_preview_box"> + <property name="height_request">250</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="av_preview_placeholder"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">12</property> + <property name="icon_name">camera-web-symbolic</property> + <property name="icon_size">6</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Live Preview</property> + <property name="justify">center</property> + <style> + <class name="bold"/> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</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> </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">10</property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <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> </object> <packing> - <property name="expand">False</property> + <property name="expand">True</property> <property name="fill">True</property> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> </object>
===================================== gajim/dialog_messages.py ===================================== @@ -71,18 +71,6 @@ messages = { _('Custom hostname "%s" is wrong. It will be ignored.'), ErrorDialog),
- 'privacy-list-error': Message( - _('Error While Removing Privacy List'), - _('Privacy list %s has not been removed. ' - 'It is probably active in one of your connected resources. ' - 'Please deactivate it and try again.'), - ErrorDialog), - - 'invisibility-not-supported': Message( - _('Invisibility Not Supported'), - _('This account %s doesn't support invisibility.'), - ErrorDialog), - 'agent-register-success': Message( _('Registration Succeeded'), _('Registration with agent %s succeeded.'),
===================================== gajim/groupchat_control.py ===================================== @@ -218,6 +218,7 @@ class GroupchatControl(ChatControlBase): ('signed-in', ged.GUI1, self._on_signed_in), ('decrypted-message-received', ged.GUI2, self._on_decrypted_message_received), ('message-sent', ged.OUT_POSTCORE, self._on_message_sent), + ('message-error', ged.GUI1, self._on_message_error), ('bookmarks-received', ged.GUI2, self._on_bookmarks_received), ]) # pylint: enable=line-too-long @@ -241,6 +242,7 @@ class GroupchatControl(ChatControlBase): # PluginSystem: adding GUI extension point for this GroupchatControl # instance object app.plugin_manager.gui_extension_point('groupchat_control', self) + self._restore_conversation()
@property def nick(self): @@ -275,8 +277,6 @@ class GroupchatControl(ChatControlBase): ('invite-', None, self._on_invite), ('contact-information-', 's', self._on_contact_information), ('execute-command-', 's', self._on_execute_command), - ('block-', 's', self._on_block), - ('unblock-', 's', self._on_unblock), ('ban-', 's', self._on_ban), ('kick-', 's', self._on_kick), ('change-role-', 'as', self._on_change_role), @@ -440,17 +440,29 @@ class GroupchatControl(ChatControlBase):
self._get_action('execute-command-').set_enabled(self.is_connected)
- block_supported = con.get_module('PrivacyLists').supported - self._get_action('block-').set_enabled(self.is_connected and - block_supported) - - self._get_action('unblock-').set_enabled(self.is_connected and - block_supported) - self._get_action('ban-').set_enabled(self.is_connected)
self._get_action('kick-').set_enabled(self.is_connected)
+ def _restore_conversation(self): + rows = app.logger.load_groupchat_messages( + self.account, self.contact.jid) + + for row in rows: + other_tags_for_name = ['muc_nickname_color_%s' % row.contact_name] + ChatControlBase.add_message(self, + row.message, + 'incoming', + row.contact_name, + float(row.time), + other_tags_for_name=other_tags_for_name, + message_id=row.message_id, + restored=True, + additional_data=row.additional_data) + + if rows: + self.conv_textview.print_empty_line() + def _is_subject_change_allowed(self): contact = app.contacts.get_gc_contact( self.account, self.room_jid, self.nick) @@ -521,11 +533,17 @@ class GroupchatControl(ChatControlBase):
def _on_invite_clicked(self, _button): invitees = self._invite_box.get_invitees() - con = app.connections[self.account] for jid in invitees: - con.get_module('MUC').invite(self.room_jid, jid) + self.invite(jid) self._show_page('groupchat')
+ def invite(self, contact_jid): + con = app.connections[self.account] + message_id = con.get_module('MUC').invite(self.room_jid, contact_jid) + self.add_info_message( + _(f'{contact_jid} has been invited to this group chat'), + message_id=message_id) + def _on_destroy_room(self, _action, _param): self.xml.destroy_reason_entry.grab_focus() self.xml.destroy_button.grab_default() @@ -661,20 +679,6 @@ class GroupchatControl(ChatControlBase): app.interface.instances[self.account]['infos'][contact.jid] = \ vcard.VcardWindow(contact, self.account, gc_contact)
- def _on_block(self, _action, param): - nick = param.get_string() - fjid = self.room_jid + '/' + nick - con = app.connections[self.account] - con.get_module('PrivacyLists').block_gc_contact(fjid) - self.roster.draw_contact(nick) - - def _on_unblock(self, _action, param): - nick = param.get_string() - fjid = self.room_jid + '/' + nick - con = app.connections[self.account] - con.get_module('PrivacyLists').unblock_gc_contact(fjid) - self.roster.draw_contact(nick) - def _on_kick(self, _action, param): nick = param.get_string() self._kick_nick = nick @@ -1780,10 +1784,7 @@ class GroupchatControl(ChatControlBase): return contact_jid = data
- con = app.connections[self.account] - con.get_module('MUC').invite(self.room_jid, contact_jid) - self.add_info_message(_('%(jid)s has been invited to this ' - 'group chat') % {'jid': contact_jid}) + self.invite(contact_jid)
def _jid_not_blocked(self, bare_jid: str) -> bool: fjid = self.room_jid + '/' + bare_jid
===================================== gajim/gtk/const.py ===================================== @@ -101,7 +101,6 @@ WINDOW_MODULES = { 'StartChatDialog': 'gajim.gtk.start_chat', 'AddNewContactWindow': 'gajim.gtk.add_contact', 'SingleMessageWindow': 'gajim.gtk.single_message', - 'PrivacyListsWindow': 'gajim.gtk.privacy_list', 'Bookmarks': 'gajim.gtk.bookmarks', 'AccountWizard': 'gajim.gtk.account_wizard', 'HistoryWindow': 'gajim.gtk.history',
===================================== gajim/gtk/preferences.py ===================================== @@ -369,9 +369,12 @@ class Preferences(Gtk.ApplicationWindow): self._ui.av_dependencies_infobar.set_no_show_all(True) self._ui.av_dependencies_infobar.hide()
- create_av_combobox('audio_input', AudioInputManager().get_devices()) - create_av_combobox('audio_output', AudioOutputManager().get_devices()) - create_av_combobox('video_input', VideoInputManager().get_devices()) + create_av_combobox( + 'audio_input', AudioInputManager().get_devices()) + create_av_combobox( + 'audio_output', AudioOutputManager().get_devices()) + create_av_combobox( + 'video_input', VideoInputManager().get_devices())
create_av_combobox( 'video_framerate', @@ -393,99 +396,22 @@ class Preferences(Gtk.ApplicationWindow): st = app.config.get('video_see_self') self._ui.video_see_self_checkbutton.set_active(st)
- def on_av_map(tab): - label = self._ui.selected_video_output - sink, widget, name = gstreamer.create_gtk_widget() - if sink is None: - log.error('Failed to obtain a working Gstreamer GTK+ sink, ' - 'video support will be disabled') - self._ui.video_input_combobox.set_sensitive(False) - label.set_markup( - _('<span color="red" font-weight="bold">' - 'Unavailable</span>, video support will be disabled')) - return - - text = '' - if name == 'gtkglsink': - text = _('<span color="green" font-weight="bold">' - 'OpenGL</span> accelerated') - elif name == 'gtksink': - text = _('<span color="yellow" font-weight="bold">' - 'Unaccelerated</span>') - label.set_markup(text) - if self.av_pipeline is None: - self.av_pipeline = Gst.Pipeline.new('preferences-pipeline') - else: - self.av_pipeline.set_state(Gst.State.NULL) - self.av_pipeline.add(sink) - self.av_sink = sink - if self.av_widget is not None: - tab.remove(self.av_widget) - tab.add(widget) - self.av_widget = widget - - src_name = app.config.get('video_input_device') - try: - self.av_src = Gst.parse_bin_from_description(src_name, True) - except GLib.Error: - log.error('Failed to parse "%s" as Gstreamer element,' - ' falling back to autovideosrc', src_name) - self.av_src = None - if self.av_src is not None: - self.av_pipeline.add(self.av_src) - self.av_src.link(self.av_sink) - self.av_pipeline.set_state(Gst.State.PLAYING) - else: - # Parsing the pipeline stored in video_input_device failed, - # let’s try the default one. - self.av_src = Gst.ElementFactory.make('autovideosrc', None) - if self.av_src is None: - log.error('Failed to obtain a working Gstreamer source,' - ' video will be disabled.') - self._ui.video_input_combobox.set_sensitive(False) - return - # Great, this succeeded, let’s store it back into the - # config and use it. We’ve made autovideosrc the first - # element in the combobox so we can pick index 0 without - # worry. - combobox = self._ui.video_input_combobox - combobox.set_active(0) - - def on_av_unmap(tab): - if self.av_pipeline is not None: - self.av_pipeline.set_state(Gst.State.NULL) - if self.av_src is not None: - self.av_pipeline.remove(self.av_src) - self.av_src = None - if self.av_sink is not None: - self.av_pipeline.remove(self.av_sink) - self.av_sink = None - if self.av_widget is not None: - tab.remove(self.av_widget) - self.av_widget = None - self.av_pipeline = None - self.av_pipeline = None self.av_src = None self.av_sink = None self.av_widget = None - tab = self._ui.audio_video_tab - tab.connect('map', on_av_map) - tab.connect('unmap', on_av_unmap) - else: for opt_name in ('audio_input', 'audio_output', 'video_input', 'video_framerate', 'video_size'): combobox = self._ui.get_object(opt_name + '_combobox') combobox.set_sensitive(False) + self._ui.live_preview_checkbutton.set_sensitive(False)
# STUN st = app.config.get('use_stun_server') self._ui.stun_checkbutton.set_active(st) - + self._ui.stun_server_entry.set_sensitive(st) self._ui.stun_server_entry.set_text(app.config.get('stun_server')) - if not st: - self._ui.stun_server_entry.set_sensitive(False)
### Advanced tab ###
@@ -994,15 +920,89 @@ class Preferences(Gtk.ApplicationWindow): widget.set_active(0) return
- self.av_pipeline.set_state(Gst.State.NULL) - if self.av_src is not None: - self.av_pipeline.remove(self.av_src) - self.av_pipeline.add(src) - src.link(self.av_sink) - self.av_src = src - self.av_pipeline.set_state(Gst.State.PLAYING) + if self._ui.live_preview_checkbutton.get_active(): + self.av_pipeline.set_state(Gst.State.NULL) + if self.av_src is not None: + self.av_pipeline.remove(self.av_src) + self.av_pipeline.add(src) + src.link(self.av_sink) + self.av_src = src + self.av_pipeline.set_state(Gst.State.PLAYING) app.config.set('video_input_device', device)
+ def _on_live_preview_toggled(self, widget): + if widget.get_active(): + sink, widget, name = gstreamer.create_gtk_widget() + if sink is None: + log.error('Failed to obtain a working Gstreamer GTK+ sink, ' + 'video support will be disabled') + self._ui.video_input_combobox.set_sensitive(False) + self._ui.selected_video_output.set_markup( + _('<span color="red" font-weight="bold">' + 'Unavailable</span>, video support will be disabled')) + return + + text = '' + if name == 'gtkglsink': + text = _('<span color="green" font-weight="bold">' + 'OpenGL</span> accelerated') + elif name == 'gtksink': + text = _('<span color="yellow" font-weight="bold">' + 'Unaccelerated</span>') + self._ui.selected_video_output.set_markup(text) + if self.av_pipeline is None: + self.av_pipeline = Gst.Pipeline.new('preferences-pipeline') + else: + self.av_pipeline.set_state(Gst.State.NULL) + self.av_pipeline.add(sink) + self.av_sink = sink + + if self.av_widget is not None: + self._ui.av_preview_box.remove(self.av_widget) + self._ui.av_preview_placeholder.set_visible(False) + self._ui.av_preview_box.add(widget) + self.av_widget = widget + + src_name = app.config.get('video_input_device') + try: + self.av_src = Gst.parse_bin_from_description(src_name, True) + except GLib.Error: + log.error('Failed to parse "%s" as Gstreamer element, ' + 'falling back to autovideosrc', src_name) + self.av_src = None + if self.av_src is not None: + self.av_pipeline.add(self.av_src) + self.av_src.link(self.av_sink) + self.av_pipeline.set_state(Gst.State.PLAYING) + else: + # Parsing the pipeline stored in video_input_device failed, + # let’s try the default one. + self.av_src = Gst.ElementFactory.make('autovideosrc', None) + if self.av_src is None: + log.error('Failed to obtain a working Gstreamer source, ' + 'video will be disabled.') + self._ui.video_input_combobox.set_sensitive(False) + return + # Great, this succeeded, let’s store it back into the + # config and use it. We’ve made autovideosrc the first + # element in the combobox so we can pick index 0 without + # worry. + self._ui.video_input_combobox.set_active(0) + else: + if self.av_pipeline is not None: + self.av_pipeline.set_state(Gst.State.NULL) + if self.av_src is not None: + self.av_pipeline.remove(self.av_src) + self.av_src = None + if self.av_sink is not None: + self.av_pipeline.remove(self.av_sink) + self.av_sink = None + if self.av_widget is not None: + self._ui.av_preview_box.remove(self.av_widget) + self._ui.av_preview_placeholder.set_visible(True) + self.av_widget = None + self.av_pipeline = None + def on_video_framerate_combobox_changed(self, widget): self.on_av_combobox_changed(widget, 'video_framerate')
===================================== gajim/gtk/privacy_list.py deleted ===================================== @@ -1,530 +0,0 @@ -# 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/. - -from gi.repository import Gdk -from gi.repository import Gtk -from gi.repository import GLib -from gi.repository import GObject - -from gajim.common import app -from gajim.common import ged -from gajim.common.i18n import _ -from gajim.common.nec import EventHelper - -from gajim.gtk.dialogs import ErrorDialog -from gajim.gtk.util import get_builder - - -class PrivacyListWindow(EventHelper): - def __init__(self, account, privacy_list_name, action): - # action is 'EDIT' or 'NEW' depending on if we create a new priv list - # or edit an already existing one - EventHelper.__init__(self) - self.account = account - self.privacy_list_name = privacy_list_name - - # Dicts and Default Values - self.active_rule = '' - self.global_rules = {} - self.list_of_groups = {} - - self.max_order = 0 - - # Default Edit Values - self.edit_rule_type = 'jid' - self.allow_deny = 'allow' - - # Connect to gtk builder - self._ui = get_builder('privacy_list_window.ui') - self.window = self._ui.get_object('privacy_list_edit_window') - - self._ui.privacy_lists_title_label.set_label( - _('Privacy List <b><i>%s</i></b>') % - GLib.markup_escape_text(self.privacy_list_name)) - - if len(app.connections) > 1: - title = _('Privacy List for %s') % self.account - else: - title = _('Privacy List') - - self._ui.delete_rule_button.set_sensitive(False) - self._ui.open_rule_button.set_sensitive(False) - self._ui.privacy_list_active_checkbutton.set_sensitive(False) - self._ui.privacy_list_default_checkbutton.set_sensitive(False) - self._ui.list_of_rules_combobox.set_sensitive(False) - - # set jabber id completion - jids_list_store = Gtk.ListStore(GObject.TYPE_STRING) - for jid in app.contacts.get_jid_list(self.account): - jids_list_store.append([jid]) - jid_entry_completion = Gtk.EntryCompletion() - jid_entry_completion.set_text_column(0) - jid_entry_completion.set_model(jids_list_store) - jid_entry_completion.set_popup_completion(True) - self._ui.edit_type_jabberid_entry.set_completion(jid_entry_completion) - if action == 'EDIT': - self.refresh_rules() - - model = self._ui.edit_type_group_combobox.get_model() - count = 0 - for group in app.groups[self.account]: - self.list_of_groups[group] = count - count += 1 - model.append([group]) - self._ui.edit_type_group_combobox.set_active(0) - - self.window.set_title(title) - - # pylint: disable=line-too-long - self.register_events([ - ('privacy-list-received', ged.GUI1, self._nec_privacy_list_received), - ('privacy-lists-received', ged.GUI1, self._nec_privacy_lists_received) - ]) - # pylint: enable=line-too-long - - self.window.show_all() - self._ui.add_edit_vbox.hide() - - self._ui.connect_signals(self) - - def on_key_press_event(self, _widget, event): - if event.keyval == Gdk.KEY_Escape: - self.window.destroy() - - def _on_edit_window_destroy(self, _widget): - key_name = 'privacy_list_%s' % self.privacy_list_name - if key_name in app.interface.instances[self.account]: - del app.interface.instances[self.account][key_name] - self.unregister_events() - - def _nec_privacy_lists_received(self, obj): - if obj.conn.name != self.account: - return - if obj.active_list == self.privacy_list_name: - self._ui.privacy_list_active_checkbutton.set_active(True) - else: - self._ui.privacy_list_active_checkbutton.set_active(False) - if obj.default_list == self.privacy_list_name: - self._ui.privacy_list_default_checkbutton.set_active(True) - else: - self._ui.privacy_list_default_checkbutton.set_active(False) - - def privacy_list_received(self, rules): - model = self._ui.list_of_rules_combobox.get_model() - model.clear() - self.global_rules = {} - for rule in rules: - if 'type' in rule: - text_item = _( - 'Order: %(order)s, action: %(action)s, type: %(type)s' - ', value: %(value)s') % { - 'order': rule['order'], - 'action': rule['action'], - 'type': rule['type'], - 'value': rule['value']} - else: - text_item = _('Order: %(order)s, action: %(action)s') % \ - {'order': rule['order'], 'action': rule['action']} - if int(rule['order']) > self.max_order: - self.max_order = int(rule['order']) - self.global_rules[text_item] = rule - model.append([text_item]) - if not rules: - self._ui.title_hbox.set_sensitive(False) - self._ui.list_of_rules_combobox.set_sensitive(False) - self._ui.delete_rule_button.set_sensitive(False) - self._ui.open_rule_button.set_sensitive(False) - self._ui.privacy_list_active_checkbutton.set_sensitive(False) - self._ui.privacy_list_default_checkbutton.set_sensitive(False) - else: - self._ui.list_of_rules_combobox.set_active(0) - self._ui.title_hbox.set_sensitive(True) - self._ui.list_of_rules_combobox.set_sensitive(True) - self._ui.delete_rule_button.set_sensitive(True) - self._ui.open_rule_button.set_sensitive(True) - self._ui.privacy_list_active_checkbutton.set_sensitive(True) - self._ui.privacy_list_default_checkbutton.set_sensitive(True) - self.reset_fields() - con = app.connections[self.account] - con.get_module('PrivacyLists').get_privacy_lists() - - def _nec_privacy_list_received(self, obj): - if obj.conn.name != self.account: - return - if obj.list_name != self.privacy_list_name: - return - self.privacy_list_received(obj.rules) - - def refresh_rules(self): - con = app.connections[self.account] - con.get_module('PrivacyLists').get_privacy_list(self.privacy_list_name) - - def on_delete_rule_button_clicked(self, _widget): - model = self._ui.list_of_rules_combobox.get_model() - iter_ = self._ui.list_of_rules_combobox.get_active_iter() - _rule = model[iter_][0] - tags = [] - for rule in self.global_rules: - if rule != _rule: - tags.append(self.global_rules[rule]) - con = app.connections[self.account] - con.get_module('PrivacyLists').set_privacy_list( - self.privacy_list_name, tags) - self.privacy_list_received(tags) - self._ui.add_edit_vbox.hide() - if not tags: # we removed latest rule - if 'privacy_lists' in app.interface.instances[self.account]: - win = app.interface.instances[self.account]['privacy_lists'] - win.remove_privacy_list(self.privacy_list_name) - win.draw_widgets() - - def on_open_rule_button_clicked(self, _widget): - self._ui.add_edit_rule_label.set_label(_('<b>Edit a rule</b>')) - active_num = self._ui.list_of_rules_combobox.get_active() - if active_num == -1: - self.active_rule = '' - else: - model = self._ui.list_of_rules_combobox.get_model() - iter_ = self._ui.list_of_rules_combobox.get_active_iter() - self.active_rule = model[iter_][0] - if self.active_rule != '': - rule_info = self.global_rules[self.active_rule] - self._ui.edit_order_spinbutton.set_value(int(rule_info['order'])) - if 'type' in rule_info: - if rule_info['type'] == 'jid': - self._ui.edit_type_jabberid_radiobutton.set_active(True) - self._ui.edit_type_jabberid_entry.set_text( - rule_info['value']) - elif rule_info['type'] == 'group': - self._ui.edit_type_group_radiobutton.set_active(True) - if rule_info['value'] in self.list_of_groups: - self._ui.edit_type_group_combobox.set_active( - self.list_of_groups[rule_info['value']]) - else: - self._ui.edit_type_group_combobox.set_active(0) - elif rule_info['type'] == 'subscription': - self._ui.edit_type_subscription_radiobutton.set_active(True) - sub_value = rule_info['value'] - if sub_value == 'none': - self._ui.edit_type_subscription_combobox.set_active(0) - elif sub_value == 'both': - self._ui.edit_type_subscription_combobox.set_active(1) - elif sub_value == 'from': - self._ui.edit_type_subscription_combobox.set_active(2) - elif sub_value == 'to': - self._ui.edit_type_subscription_combobox.set_active(3) - else: - self._ui.edit_type_select_all_radiobutton.set_active(True) - else: - self._ui.edit_type_select_all_radiobutton.set_active(True) - self._ui.edit_send_messages_checkbutton.set_active(False) - self._ui.edit_queries_send_checkbutton.set_active(False) - self._ui.edit_view_status_checkbutton.set_active(False) - self._ui.edit_send_status_checkbutton.set_active(False) - self._ui.edit_all_checkbutton.set_active(False) - if not rule_info['child']: - self._ui.edit_all_checkbutton.set_active(True) - else: - if 'presence-out' in rule_info['child']: - self._ui.edit_send_status_checkbutton.set_active(True) - if 'presence-in' in rule_info['child']: - self._ui.edit_view_status_checkbutton.set_active(True) - if 'iq' in rule_info['child']: - self._ui.edit_queries_send_checkbutton.set_active(True) - if 'message' in rule_info['child']: - self._ui.edit_send_messages_checkbutton.set_active(True) - - if rule_info['action'] == 'allow': - self._ui.edit_allow_radiobutton.set_active(True) - else: - self._ui.edit_deny_radiobutton.set_active(True) - self._ui.add_edit_vbox.show() - - def on_edit_all_checkbutton_toggled(self, widget): - if widget.get_active(): - self._ui.edit_send_messages_checkbutton.set_active(True) - self._ui.edit_queries_send_checkbutton.set_active(True) - self._ui.edit_view_status_checkbutton.set_active(True) - self._ui.edit_send_status_checkbutton.set_active(True) - self._ui.edit_send_messages_checkbutton.set_sensitive(False) - self._ui.edit_queries_send_checkbutton.set_sensitive(False) - self._ui.edit_view_status_checkbutton.set_sensitive(False) - self._ui.edit_send_status_checkbutton.set_sensitive(False) - else: - self._ui.edit_send_messages_checkbutton.set_active(False) - self._ui.edit_queries_send_checkbutton.set_active(False) - self._ui.edit_view_status_checkbutton.set_active(False) - self._ui.edit_send_status_checkbutton.set_active(False) - self._ui.edit_send_messages_checkbutton.set_sensitive(True) - self._ui.edit_queries_send_checkbutton.set_sensitive(True) - self._ui.edit_view_status_checkbutton.set_sensitive(True) - self._ui.edit_send_status_checkbutton.set_sensitive(True) - - def _on_privacy_list_active_toggled(self, widget): - name = None - if widget.get_active(): - name = self.privacy_list_name - - con = app.connections[self.account] - con.get_module('PrivacyLists').set_active_list(name) - - def _on_privacy_list_default_toggled(self, widget): - name = None - if widget.get_active(): - name = self.privacy_list_name - - con = app.connections[self.account] - con.get_module('PrivacyLists').set_default_list(name) - - def on_new_rule_button_clicked(self, _widget): - self.reset_fields() - self._ui.add_edit_vbox.show() - - def reset_fields(self): - self._ui.edit_type_jabberid_entry.set_text('') - self._ui.edit_allow_radiobutton.set_active(True) - self._ui.edit_type_jabberid_radiobutton.set_active(True) - self.active_rule = '' - self._ui.edit_send_messages_checkbutton.set_active(False) - self._ui.edit_queries_send_checkbutton.set_active(False) - self._ui.edit_view_status_checkbutton.set_active(False) - self._ui.edit_send_status_checkbutton.set_active(False) - self._ui.edit_all_checkbutton.set_active(False) - self._ui.edit_order_spinbutton.set_value(self.max_order + 1) - self._ui.edit_type_group_combobox.set_active(0) - self._ui.edit_type_subscription_combobox.set_active(0) - self._ui.add_edit_rule_label.set_label(_('<b>Add a rule</b>')) - - def get_current_tags(self): - if self._ui.edit_type_jabberid_radiobutton.get_active(): - edit_type = 'jid' - edit_value = self._ui.edit_type_jabberid_entry.get_text() - elif self._ui.edit_type_group_radiobutton.get_active(): - edit_type = 'group' - model = self._ui.edit_type_group_combobox.get_model() - iter_ = self._ui.edit_type_group_combobox.get_active_iter() - edit_value = model[iter_][0] - elif self._ui.edit_type_subscription_radiobutton.get_active(): - edit_type = 'subscription' - subs = ['none', 'both', 'from', 'to'] - edit_value = subs[ - self._ui.edit_type_subscription_combobox.get_active()] - elif self._ui.edit_type_select_all_radiobutton.get_active(): - edit_type = '' - edit_value = '' - edit_order = str(self._ui.edit_order_spinbutton.get_value_as_int()) - if self._ui.edit_allow_radiobutton.get_active(): - edit_deny = 'allow' - else: - edit_deny = 'deny' - child = [] - if not self._ui.edit_all_checkbutton.get_active(): - if self._ui.edit_send_messages_checkbutton.get_active(): - child.append('message') - if self._ui.edit_queries_send_checkbutton.get_active(): - child.append('iq') - if self._ui.edit_send_status_checkbutton.get_active(): - child.append('presence-out') - if self._ui.edit_view_status_checkbutton.get_active(): - child.append('presence-in') - if edit_type != '': - return {'order': edit_order, 'action': edit_deny, - 'type': edit_type, 'value': edit_value, 'child': child} - return {'order': edit_order, 'action': edit_deny, 'child': child} - - def on_save_rule_button_clicked(self, _widget): - tags = [] - current_tags = self.get_current_tags() - if int(current_tags['order']) > self.max_order: - self.max_order = int(current_tags['order']) - if self.active_rule == '': - tags.append(current_tags) - - for rule in self.global_rules: - if rule != self.active_rule: - tags.append(self.global_rules[rule]) - else: - tags.append(current_tags) - - con = app.connections[self.account] - con.get_module('PrivacyLists').set_privacy_list( - self.privacy_list_name, tags) - self.refresh_rules() - self._ui.add_edit_vbox.hide() - if 'privacy_lists' in app.interface.instances[self.account]: - win = app.interface.instances[self.account]['privacy_lists'] - win.add_privacy_list_to_combobox(self.privacy_list_name) - win.draw_widgets() - - def _on_list_of_rules_changed(self, _widget): - self._ui.add_edit_vbox.hide() - - def _on_edit_type_changed(self, widget, radiobutton): - active_bool = widget.get_active() - if active_bool: - self.edit_rule_type = radiobutton - - def _on_edit_allow_changed(self, widget, radiobutton): - active_bool = widget.get_active() - if active_bool: - self.allow_deny = radiobutton - - def _on_close_clicked(self, _widget): - self.window.destroy() - - -class PrivacyListsWindow(EventHelper): - """ - Window that is the main window for Privacy Lists; we can list there the - privacy lists and ask to create a new one or edit an already there one - """ - def __init__(self, account): - EventHelper.__init__(self) - self.account = account - self.privacy_lists_save = [] - - self._ui = get_builder('privacy_lists_window.ui') - - self.window = self._ui.get_object('privacy_lists_first_window') - - self.draw_privacy_lists_in_combobox([]) - self.privacy_lists_refresh() - - self.enabled = True - - if len(app.connections) > 1: - title = _('Privacy Lists for %s') % self.account - else: - title = _('Privacy Lists') - - self.window.set_title(title) - - # pylint: disable=line-too-long - self.register_events([ - ('privacy-lists-received', ged.GUI1, self._nec_privacy_lists_received), - ('privacy-list-removed', ged.GUI1, self._nec_privacy_lists_removed), - ]) - # pylint: enable=line-too-long - - self.window.show_all() - - self._ui.connect_signals(self) - - def on_key_press_event(self, _widget, event): - if event.keyval == Gdk.KEY_Escape: - self.window.destroy() - - def _on_privacy_lists_first_destroy(self, _widget): - if 'privacy_lists' in app.interface.instances[self.account]: - del app.interface.instances[self.account]['privacy_lists'] - self.unregister_events() - - def remove_privacy_list(self, privacy_list): - if privacy_list not in self.privacy_lists_save: - return - - model = self._ui.list_of_privacy_lists_combobox.get_model() - for entry in model: - if entry[0] == privacy_list: - model.remove(entry.iter) - - self.privacy_lists_save.remove(privacy_list) - - def add_privacy_list_to_combobox(self, privacy_list): - if privacy_list in self.privacy_lists_save: - return - model = self._ui.list_of_privacy_lists_combobox.get_model() - model.append([privacy_list]) - self.privacy_lists_save.append(privacy_list) - - def draw_privacy_lists_in_combobox(self, privacy_lists): - self._ui.list_of_privacy_lists_combobox.set_active(-1) - self._ui.list_of_privacy_lists_combobox.get_model().clear() - self.privacy_lists_save = [] - for add_item in privacy_lists: - self.add_privacy_list_to_combobox(add_item) - self.draw_widgets() - - def draw_widgets(self): - if not self.privacy_lists_save: - self._ui.list_of_privacy_lists_combobox.set_sensitive(False) - self._ui.open_privacy_list_button.set_sensitive(False) - self._ui.delete_privacy_list_button.set_sensitive(False) - else: - self._ui.list_of_privacy_lists_combobox.set_sensitive(True) - self._ui.list_of_privacy_lists_combobox.set_active(0) - self._ui.open_privacy_list_button.set_sensitive(True) - self._ui.delete_privacy_list_button.set_sensitive(True) - - def _on_close_clicked(self, _widget): - self.window.destroy() - - def _on_delete_privacy_list_clicked(self, _widget): - active_list = self.privacy_lists_save[ - self._ui.list_of_privacy_lists_combobox.get_active()] - con = app.connections[self.account] - con.get_module('PrivacyLists').del_privacy_list(active_list) - - def privacy_list_removed(self, active_list): - self.privacy_lists_save.remove(active_list) - self.privacy_lists_received(self.privacy_lists_save) - - def _nec_privacy_lists_removed(self, obj): - if obj.conn.name != self.account: - return - self.privacy_list_removed(obj.list_name) - - def privacy_lists_received(self, lists): - privacy_lists = [] - for privacy_list in lists: - privacy_lists.append(privacy_list) - self.draw_privacy_lists_in_combobox(privacy_lists) - - def _nec_privacy_lists_received(self, obj): - if obj.conn.name != self.account: - return - self.privacy_lists_received(obj.lists) - - def privacy_lists_refresh(self): - con = app.connections[self.account] - con.get_module('PrivacyLists').get_privacy_lists() - - def _on_new_privacy_list_clicked(self, _widget): - name = self._ui.new_privacy_list_entry.get_text() - if not name: - ErrorDialog( - _('Invalid List Name'), - _('You must enter a name to create a privacy list.'), - transient_for=self.window) - return - key_name = 'privacy_list_%s' % name - if key_name in app.interface.instances[self.account]: - app.interface.instances[self.account][key_name].window.present() - else: - app.interface.instances[self.account][key_name] = \ - PrivacyListWindow(self.account, name, 'NEW') - self._ui.new_privacy_list_entry.set_text('') - - def _on_privacy_lists_refresh_clicked(self, _widget): - self.privacy_lists_refresh() - - def _on_open_privacy_list_clicked(self, _widget): - name = self.privacy_lists_save[ - self._ui.list_of_privacy_lists_combobox.get_active()] - key_name = 'privacy_list_%s' % name - if key_name in app.interface.instances[self.account]: - app.interface.instances[self.account][key_name].window.present() - else: - app.interface.instances[self.account][key_name] = \ - PrivacyListWindow(self.account, name, 'EDIT')
===================================== gajim/gtk/server_info.py ===================================== @@ -258,8 +258,6 @@ class ServerInfo(Gtk.ApplicationWindow, EventHelper): max_file_size
return [ - Feature('XEP-0016: Privacy Lists', - con.get_module('PrivacyLists').supported), Feature('XEP-0045: Multi-User Chat', con.get_module('MUC').supported), Feature('XEP-0054: vcard-temp',
===================================== gajim/gui_menu_builder.py ===================================== @@ -391,8 +391,7 @@ control=None, gc_contact=None, is_anonymous=True):
con = app.connections[account] - if con and (con.get_module('PrivacyLists').supported or - con.get_module('Blocking').supported): + if con.get_module('Blocking').supported: transport = app.get_transport_name_from_jid(jid, use_config_setting=False) if helpers.jid_is_blocked(account, jid): block_menuitem.set_no_show_all(True) @@ -522,6 +521,7 @@ def get_singlechat_menu(control_id, account, jid): (_('Send Chatstate'), ['chatstate']), ('win.invite-contacts-', _('Invite Contacts…')), ('win.add-to-roster-', _('Add to Contact List…')), + ('win.block-contact-', _('Block Contact…')), ('win.toggle-audio-', _('Voice Chat')), ('win.toggle-video-', _('Video Chat')), ('win.information-', _('Information')), @@ -680,7 +680,6 @@ def get_account_menu(account): ('-bookmarks', _('Bookmarks')), ('-pep-config', _('PEP Configuration')), ('-sync-history', _('Synchronise History…')), - ('-privacylists', _('Privacy Lists')), ]), (_('Admin'), [ ('-send-server-message', _('Send Server Message…')), @@ -871,17 +870,6 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
menu.append(Gtk.SeparatorMenuItem())
- if helpers.jid_is_blocked(account, contact.get_full_jid()): - action = 'win.unblock-%s::%s' % (control_id, contact.name) - label = _('Unblock') - else: - action = 'win.block-%s::%s' % (control_id, contact.name) - label = _('Block') - - item = Gtk.MenuItem(label=label) - item.set_detailed_action_name(action) - menu.append(item) - item = Gtk.MenuItem(label=_('Kick')) action = 'win.kick-%s::%s' % (control_id, contact.name) if is_role_change_allowed(self_contact, contact):
===================================== gajim/roster_window.py ===================================== @@ -1073,8 +1073,6 @@ class RosterWindow: else: accounts = [account] text = GLib.markup_escape_text(group) - if helpers.group_is_blocked(account, group): - text = '<span strikethrough="true">%s</span>' % text if app.config.get('show_contacts_number'): nbr_on, nbr_total = app.contacts.get_nb_online_total_contacts( accounts=accounts, groups=[group]) @@ -1165,14 +1163,7 @@ class RosterWindow: name = '%s [%s]' % (name, str(nb_unread))
# Strike name if blocked - strike = False - if helpers.jid_is_blocked(account, jid): - strike = True - else: - for group in contact.get_shown_groups(): - if helpers.group_is_blocked(account, group): - strike = True - break + strike = helpers.jid_is_blocked(account, jid) if strike: name = '<span strikethrough="true">%s</span>' % name
@@ -2744,55 +2735,40 @@ class RosterWindow: for jid in obj.changed: self.draw_contact(jid, obj.conn.name)
- def on_block(self, widget, list_, group=None): + def on_block(self, widget, list_): """ When clicked on the 'block' button in context menu. list_ is a list of (contact, account) """ - def on_continue(msg, pep_dict): - if msg is None: - # user pressed Cancel to change status message dialog - return + def _block_it(is_checked=None, report=None): + if is_checked is not None: # Dialog has been shown + if is_checked: + app.config.set('confirm_block', 'no') + else: + app.config.set('confirm_block', 'yes')
accounts = [] for _, account in list_: con = app.connections[account] - if con.get_module('PrivacyLists').supported or ( - group is None and con.get_module('Blocking').supported): + if con.get_module('Blocking').supported: accounts.append(account)
- if group is None: - for acct in accounts: - l_ = [i[0] for i in list_ if i[1] == acct] - con = app.connections[acct] - con.get_module('PrivacyLists').block_contacts(l_, msg) - for contact in l_: - ctrl = app.interface.msg_win_mgr.get_control( - contact.jid, acct) - if ctrl: - ctrl.parent_win.remove_tab( - ctrl, ctrl.parent_win.CLOSE_COMMAND, force=True) - if contact.show == 'not in roster': - self.remove_contact(contact.jid, acct, force=True, - backend=True) - return - self.draw_contact(contact.jid, acct) - else: - for acct in accounts: - l_ = [i[0] for i in list_ if i[1] == acct] - con = app.connections[acct] - con.get_module('PrivacyLists').block_group(group, l_, msg) - self.draw_group(group, acct) - for contact in l_: - self.draw_contact(contact.jid, acct) - - def _block_it(is_checked=None): - if is_checked is not None: # dialog has been shown - if is_checked: # user does not want to be asked again - app.config.set('confirm_block', 'no') - else: - app.config.set('confirm_block', 'yes') - self.get_status_message('offline', on_continue, show_pep=False) + for acct in accounts: + l_ = [i[0] for i in list_ if i[1] == acct] + con = app.connections[acct] + jid_list = [contact.jid for contact in l_] + con.get_module('Blocking').block(jid_list, report) + for contact in l_: + ctrl = app.interface.msg_win_mgr.get_control( + contact.jid, acct) + if ctrl: + ctrl.parent_win.remove_tab( + ctrl, ctrl.parent_win.CLOSE_COMMAND, force=True) + if contact.show == 'not in roster': + self.remove_contact(contact.jid, acct, force=True, + backend=True) + return + self.draw_contact(contact.jid, acct)
# Check if confirmation is needed for blocking confirm_block = app.config.get('confirm_block') @@ -2800,61 +2776,39 @@ class RosterWindow: _block_it() return
- # Set dialog's labels depending on if it is a group or a single contact - if group is None: - title = _('Block Contact') - pritext = _('Really block this contact?') - sectext = _('This contact will see you offline and you will not ' - 'receive any messages sent to you by this contact.') - button_text = _('_Block Contact') - else: - title = _('Block Group') - pritext = _('Really block this group?') - sectext = _('All contacts of this group will see you as offline ' - 'and you will not receive any messages sent to you ' - 'by any one of these contacts.') - button_text = _('_Block Group') - NewConfirmationCheckDialog( - title, - pritext, - sectext, - _('_Do not ask me again'), + _('Block Contact'), + _('Really block this contact?'), + _('You will appear offline for this contact and you ' + 'will not receive further messages.'), + _('_Do not ask again'), [DialogButton.make('Cancel'), + DialogButton.make('OK', + text=_('_Report Spam'), + callback=_block_it, + kwargs={'report': 'spam'}), DialogButton.make('Remove', - text=button_text, + text=_('_Block'), callback=_block_it)], modal=False).show()
- def on_unblock(self, widget, list_, group=None): + def on_unblock(self, widget, list_): """ When clicked on the 'unblock' button in context menu. """ accounts = [] for _, account in list_: con = app.connections[account] - if con.get_module('PrivacyLists').supported or ( - group is None and con.get_module('Blocking').supported): + if con.get_module('Blocking').supported: accounts.append(account)
- if group is None: - for acct in accounts: - l_ = [i[0] for i in list_ if i[1] == acct] - con = app.connections[acct] - con.get_module('PrivacyLists').unblock_contacts(l_) - for contact in l_: - self.draw_contact(contact.jid, acct) - else: - for acct in accounts: - l_ = [i[0] for i in list_ if i[1] == acct] - con = app.connections[acct] - con.get_module('PrivacyLists').unblock_group(group, l_) - self.draw_group(group, acct) - for contact in l_: - self.draw_contact(contact.jid, acct) for acct in accounts: - if 'privacy_list_block' in app.interface.instances[acct]: - del app.interface.instances[acct]['privacy_list_block'] + l_ = [i[0] for i in list_ if i[1] == acct] + con = app.connections[acct] + jid_list = [contact.jid for contact in l_] + con.get_module('Blocking').unblock(jid_list) + for contact in l_: + self.draw_contact(contact.jid, acct)
def on_rename(self, widget, row_type, jid, account): # This function is called either by F2 or by Rename menuitem @@ -2967,24 +2921,24 @@ class RosterWindow: app.interface.instances['file_transfers'].show_file_send_request( account, contact)
- def on_invite_to_room(self, widget, list_, room_jid, room_account, - resource=None): + def on_invite_to_room(self, + _widget, + list_, + room_jid, + room_account, + resource=None): """ Resource parameter MUST NOT be used if more than one contact in list """ - for e in list_: - contact = e[0] + gc_control = app.get_groupchat_control(room_account, room_jid) + if gc_control is None: + return + + for contact, _ in list_: contact_jid = contact.jid if resource: # we MUST have one contact only in list_ contact_jid += '/' + resource - con = app.connections[room_account] - con.get_module('MUC').invite(room_jid, contact_jid) - gc_control = app.interface.msg_win_mgr.get_gc_control(room_jid, - room_account) - if gc_control: - gc_control.add_info_message( - _('%(jid)s has been invited to this group chat') % { - 'jid': contact_jid}) + gc_control.invite(contact_jid)
def on_all_groupchat_maximized(self, widget, group_list): for (contact, account) in group_list: @@ -4204,8 +4158,9 @@ class RosterWindow: if type_source != 'contact': return contact_jid = data - con = app.connections[account_dest] - con.get_module('MUC').invite(jid_dest, contact_jid) + gc_control = app.get_groupchat_control(account_dest, jid_dest) + if gc_control is not None: + gc_control.invite(contact_jid) return
if type_source == 'group': @@ -4902,28 +4857,6 @@ class RosterWindow: rename_item.connect('activate', self.on_rename, 'group', group, account)
- # Block group - is_blocked = False - if self.regroup: - for g_account in app.connections: - if helpers.group_is_blocked(g_account, group): - is_blocked = True - else: - if helpers.group_is_blocked(account, group): - is_blocked = True - - if is_blocked and app.connections[account].get_module('PrivacyLists').supported: - unblock_menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Unblock')) - unblock_menuitem.connect('activate', self.on_unblock, list_, - group) - menu.append(unblock_menuitem) - else: - block_menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Block')) - block_menuitem.connect('activate', self.on_block, list_, group) - menu.append(block_menuitem) - if not app.connections[account].get_module('PrivacyLists').supported: - block_menuitem.set_sensitive(False) - # Remove group remove_item = Gtk.MenuItem.new_with_mnemonic(_('Remo_ve')) menu.append(remove_item) @@ -4967,16 +4900,18 @@ class RosterWindow: list_ = [] # list of (jid, account) tuples one_account_offline = False is_blocked = True - privacy_rules_supported = True + blocking_supported = True for titer in iters: jid = model[titer][Column.JID] account = model[titer][Column.ACCOUNT] if not app.account_is_available(account): one_account_offline = True - if not app.connections[account].get_module('PrivacyLists').supported: - privacy_rules_supported = False - contact = app.contacts.get_contact_with_highest_priority(account, - jid) + + con = app.connections[account] + if not con.get_module('Blocking').supported: + blocking_supported = False + contact = app.contacts.get_contact_with_highest_priority( + account, jid) if not helpers.jid_is_blocked(account, jid): is_blocked = False list_.append((contact, account)) @@ -5027,7 +4962,7 @@ class RosterWindow: manage_contacts_submenu.append(item)
# Block - if is_blocked and privacy_rules_supported: + if is_blocked and blocking_supported: unblock_menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Unblock')) unblock_menuitem.connect('activate', self.on_unblock, list_) manage_contacts_submenu.append(unblock_menuitem) @@ -5036,7 +4971,7 @@ class RosterWindow: block_menuitem.connect('activate', self.on_block, list_) manage_contacts_submenu.append(block_menuitem)
- if not privacy_rules_supported: + if not blocking_supported: block_menuitem.set_sensitive(False)
# Remove @@ -5122,75 +5057,6 @@ class RosterWindow: menu.show_all() menu.popup(None, None, None, None, event_button, event.time)
- def get_and_connect_advanced_menuitem_menu(self, account): - """ - Add FOR ACCOUNT options - """ - xml = get_builder('advanced_menuitem_menu.ui') - advanced_menuitem_menu = xml.get_object('advanced_menuitem_menu') - - xml_console_menuitem = xml.get_object('xml_console_menuitem') - archiving_preferences_menuitem = xml.get_object( - 'archiving_preferences_menuitem') - privacy_lists_menuitem = xml.get_object('privacy_lists_menuitem') - administrator_menuitem = xml.get_object('administrator_menuitem') - send_server_message_menuitem = xml.get_object( - 'send_server_message_menuitem') - set_motd_menuitem = xml.get_object('set_motd_menuitem') - update_motd_menuitem = xml.get_object('update_motd_menuitem') - delete_motd_menuitem = xml.get_object('delete_motd_menuitem') - - xml_console_menuitem.connect('activate', - self.on_xml_console_menuitem_activate, account) - - if app.connections[account]: - if app.connections[account].get_module('PrivacyLists').supported: - privacy_lists_menuitem.connect('activate', - self.on_privacy_lists_menuitem_activate, account) - else: - privacy_lists_menuitem.set_sensitive(False) - if app.connections[account].get_module('MAM').available: - archiving_preferences_menuitem.connect( - 'activate', - self.on_archiving_preferences_menuitem_activate, account) - else: - archiving_preferences_menuitem.set_sensitive(False) - - if app.connections[account].is_zeroconf: - administrator_menuitem.set_sensitive(False) - send_server_message_menuitem.set_sensitive(False) - set_motd_menuitem.set_sensitive(False) - update_motd_menuitem.set_sensitive(False) - delete_motd_menuitem.set_sensitive(False) - else: - send_server_message_menuitem.connect('activate', - self.on_send_server_message_menuitem_activate, account) - - set_motd_menuitem.connect('activate', - self.on_set_motd_menuitem_activate, account) - - update_motd_menuitem.connect('activate', - self.on_update_motd_menuitem_activate, account) - - delete_motd_menuitem.connect('activate', - self.on_delete_motd_menuitem_activate, account) - - advanced_menuitem_menu.show_all() - - return advanced_menuitem_menu - - def add_history_manager_menuitem(self, menu): - """ - Add a separator and History Manager menuitem BELOW for account menuitems - """ - item = Gtk.SeparatorMenuItem.new() # separator - menu.append(item) - - # History manager - item = Gtk.MenuItem.new_with_mnemonic(_('History Manager')) - menu.append(item) - item.connect('activate', self.on_history_manager_menuitem_activate) - def show_appropriate_context_menu(self, event, iters): # iters must be all of the same type model = self.modelfilter
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/12b818602ce2a797cd7cbe95fe47fa5e...