From 76f7d45b46b23348fa8db19da6dfafdb8458e0dc Mon Sep 17 00:00:00 2001 From: "W. Martin Borgert" Date: Tue, 26 Sep 2017 00:14:40 +0200 Subject: [PATCH] Imported upstream master from 2017-09-25 --- .gitlab-ci.yml | 5 +- gajim/accounts_window.py | 8 +- gajim/app_actions.py | 6 + gajim/chat_control.py | 10 +- gajim/common/app.py | 13 +- gajim/common/config.py | 1 - gajim/common/configpaths.py | 2 +- gajim/common/connection_handlers.py | 21 + gajim/common/dbus_support.py | 21 - gajim/common/zeroconf/connection_zeroconf.py | 6 +- gajim/features_window.py | 16 - gajim/gajim.py | 5 +- gajim/groupchat_control.py | 10 +- gajim/gui_interface.py | 13 +- gajim/notify.py | 417 ++----------------- setup.py | 2 +- 16 files changed, 115 insertions(+), 441 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eba492fb0..53de81757 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,11 +29,12 @@ run-build: - tar xzf $FN.gz - rm $FN.gz - export GF=$(find . -maxdepth 1 -type d -name 'gajim-*') - - cd $GF/gajim/plugins/ + - mkdir -p $GF/gajim/data/plugins/ + - cd $GF/gajim/data/plugins/ - curl -O https://ftp.gajim.org/plugins_1/plugin_installer.zip - unzip plugin_installer.zip - rm plugin_installer.zip - - cd ../../.. + - cd ../../../.. - tar czf ../$FN.gz gajim-* - cd .. - rm -rf tmp_add_plugins diff --git a/gajim/accounts_window.py b/gajim/accounts_window.py index 3b4f3df1a..b029bf301 100644 --- a/gajim/accounts_window.py +++ b/gajim/accounts_window.py @@ -556,8 +556,8 @@ class ZeroConfPage(GenericOptionPage): def __init__(self, account, parent=None): options = [ - Option(OptionKind.DIALOG, _('Credentials'), - OptionType.DIALOG, props={'dialog': CredentialsDialog}), + Option(OptionKind.DIALOG, _('Profile'), + OptionType.DIALOG, props={'dialog': ZeroconfProfileDialog}), Option(OptionKind.SWITCH, _('Connect on startup'), OptionType.ACCOUNT_CONFIG, 'autoconnect', @@ -578,7 +578,7 @@ class ZeroConfPage(GenericOptionPage): GenericOptionPage.__init__(self, account, parent, options) -class CredentialsDialog(OptionsDialog): +class ZeroconfProfileDialog(OptionsDialog): def __init__(self, account, parent): options = [ @@ -595,7 +595,7 @@ class CredentialsDialog(OptionsDialog): OptionType.ACCOUNT_CONFIG, 'zeroconf_email'), ] - OptionsDialog.__init__(self, parent, _('Credential Options'), + OptionsDialog.__init__(self, parent, _('Profile'), Gtk.DialogFlags.MODAL, options, account) diff --git a/gajim/app_actions.py b/gajim/app_actions.py index e718dfa58..8b7ae5cb3 100644 --- a/gajim/app_actions.py +++ b/gajim/app_actions.py @@ -266,3 +266,9 @@ class AppActions(): else: interface.instances['logs'] = history_window.\ HistoryWindow() + + def on_open_event(self, action, param): + dict_ = param.unpack() + app.interface.handle_event(dict_['account'], dict_['jid'], + dict_['type_']) + diff --git a/gajim/chat_control.py b/gajim/chat_control.py index 57a4493ee..8631c0796 100644 --- a/gajim/chat_control.py +++ b/gajim/chat_control.py @@ -856,18 +856,18 @@ class ChatControl(ChatControlBase): self._show_lock_image(**encryption_state) - def _show_lock_image(self, visible, enc_type='', - authenticated=False): + def _show_lock_image(self, visible, enc_type='', authenticated=False): """ Set lock icon visibility and create tooltip """ if authenticated: authenticated_string = _('and authenticated') - img_path = gtkgui_helpers.get_icon_path('security-high') + self.lock_image.set_from_icon_name( + 'security-high', Gtk.IconSize.MENU) else: authenticated_string = _('and NOT authenticated') - img_path = gtkgui_helpers.get_icon_path('security-low') - self.lock_image.set_from_file(img_path) + self.lock_image.set_from_icon_name( + 'security-low', Gtk.IconSize.MENU) tooltip = _('%(type)s encryption is active %(authenticated)s.') % {'type': enc_type, 'authenticated': authenticated_string} diff --git a/gajim/common/app.py b/gajim/common/app.py index 7f6a6e105..1751f452e 100644 --- a/gajim/common/app.py +++ b/gajim/common/app.py @@ -53,7 +53,7 @@ ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher nec = None # Network Events Controller plugin_manager = None # Plugins Manager -log = logging.getLogger('gajim') +glog = logging.getLogger('gajim') logger = None @@ -174,7 +174,7 @@ try: ''' v_gnupg = gnupg.__version__ if V(v_gnupg) < V('0.3.8') or V(v_gnupg) > V('1.0.0'): - log.info('Gajim needs python-gnupg >= 0.3.8') + glog.info('Gajim needs python-gnupg >= 0.3.8') HAVE_GPG = False except ImportError: HAVE_GPG = False @@ -245,7 +245,7 @@ try: if sleepy.SUPPORTED: HAVE_IDLE = True except Exception: - log.debug(_('Unable to load idle module')) + glog.info(_('Unable to load idle module')) HAVE_IDLE = False @@ -259,7 +259,8 @@ gajim_common_features = [nbxmpp.NS_BYTESTREAM, nbxmpp.NS_SI, nbxmpp.NS_FILE, nbxmpp.NS_SSN, nbxmpp.NS_MOOD, nbxmpp.NS_ACTIVITY, nbxmpp.NS_NICK, nbxmpp.NS_ROSTERX, nbxmpp.NS_SECLABEL, nbxmpp.NS_HASHES_2, nbxmpp.NS_HASHES_MD5, nbxmpp.NS_HASHES_SHA1, nbxmpp.NS_HASHES_SHA256, - nbxmpp.NS_HASHES_SHA512, nbxmpp.NS_CONFERENCE, nbxmpp.NS_CORRECT] + nbxmpp.NS_HASHES_SHA512, nbxmpp.NS_CONFERENCE, nbxmpp.NS_CORRECT, + nbxmpp.NS_EME] # Optional features gajim supports per account gajim_optional_features = {} @@ -491,3 +492,7 @@ def get_priority(account, show): elif prio > 127: prio = 127 return prio + +def log(domain): + root = 'gajim.' + return logging.getLogger(root + domain) diff --git a/gajim/common/config.py b/gajim/common/config.py index ad5eb0580..e99777b7d 100644 --- a/gajim/common/config.py +++ b/gajim/common/config.py @@ -75,7 +75,6 @@ class Config: 'autopopupaway': [ opt_bool, False ], 'autopopup_chat_opened': [ opt_bool, False, _('Show desktop notification even when a chat window is opened for this contact and does not have focus') ], 'sounddnd': [ opt_bool, False, _('Play sound when user is busy')], - 'use_notif_daemon': [ opt_bool, True, _('Use D-Bus and Notification-Daemon to show notifications') ], 'showoffline': [ opt_bool, False ], 'show_only_chat_and_online': [ opt_bool, False, _('Show only online and free for chat contacts in roster.')], 'show_transports_group': [ opt_bool, True ], diff --git a/gajim/common/configpaths.py b/gajim/common/configpaths.py index 22eceeba5..7a8c2c780 100644 --- a/gajim/common/configpaths.py +++ b/gajim/common/configpaths.py @@ -103,7 +103,7 @@ class ConfigPaths: self.add('GUI', None, os.path.join(basedir, 'data', 'gui')) self.add('ICONS', None, os.path.join(basedir, 'data', 'icons')) self.add('HOME', None, os.path.expanduser('~')) - self.add('PLUGINS_BASE', None, os.path.join(basedir, 'plugins')) + self.add('PLUGINS_BASE', None, os.path.join(basedir, 'data', 'plugins')) def add(self, name, type_, path): self.paths[name] = (type_, path) diff --git a/gajim/common/connection_handlers.py b/gajim/common/connection_handlers.py index 033202602..9ec8da157 100644 --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -1080,6 +1080,27 @@ class ConnectionHandlersBase: app.plugin_manager.extension_point( 'decrypt', self, obj, self._on_message_received) if not obj.encrypted: + # XEP-0380 + enc_tag = obj.stanza.getTag('encryption', namespace=nbxmpp.NS_EME) + if enc_tag: + ns = enc_tag.getAttr('namespace') + if ns: + if ns == 'urn:xmpp:otr:0': + obj.msgtxt = _('This message was encrypted with OTR ' + 'and could not be decrypted.') + elif ns == 'jabber:x:encrypted': + obj.msgtxt = _('This message was encrypted with Legacy ' + 'OpenPGP and could not be decrypted. You can install ' + 'the PGP plugin to handle those messages.') + elif ns == 'urn:xmpp:openpgp:0': + obj.msgtxt = _('This message was encrypted with ' + 'OpenPGP for XMPP and could not be decrypted.') + else: + enc_name = enc_tag.getAttr('name') + if not enc_name: + enc_name = ns + obj.msgtxt = _('This message was encrypted with %s ' + 'and could not be decrypted.') % enc_name self._on_message_received(obj) def _on_message_received(self, obj): diff --git a/gajim/common/dbus_support.py b/gajim/common/dbus_support.py index c8476551b..77b45e753 100644 --- a/gajim/common/dbus_support.py +++ b/gajim/common/dbus_support.py @@ -159,27 +159,6 @@ def get_interface(interface, path, start_service=True): return None -def get_notifications_interface(notif=None): - """ - Get the notifications interface - - :param notif: DesktopNotification instance - """ - # try to see if KDE notifications are available - iface = get_interface('org.kde.VisualNotifications', '/VisualNotifications', - start_service=False) - if iface != None: - if notif != None: - notif.kde_notifications = True - return iface - # KDE notifications don't seem to be available, falling back to - # notification-daemon - else: - if notif != None: - notif.kde_notifications = False - return get_interface('org.freedesktop.Notifications', - '/org/freedesktop/Notifications') - if supported: class MissingArgument(dbus.DBusException): _dbus_error_name = _GAJIM_ERROR_IFACE + '.MissingArgument' diff --git a/gajim/common/zeroconf/connection_zeroconf.py b/gajim/common/zeroconf/connection_zeroconf.py index 10220a6e1..5eeb029c2 100644 --- a/gajim/common/zeroconf/connection_zeroconf.py +++ b/gajim/common/zeroconf/connection_zeroconf.py @@ -50,6 +50,8 @@ from gajim.common.zeroconf import zeroconf from gajim.common.zeroconf.connection_handlers_zeroconf import * from gajim.common.connection_handlers_events import * +log = logging.getLogger('gajim.c.connection_zeroconf') + class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf): def __init__(self, name): ConnectionHandlersZeroconf.__init__(self) @@ -73,7 +75,7 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf): values """ if not app.config.get_per('accounts', app.ZEROCONF_ACC_NAME, 'name'): - app.log.debug('Creating zeroconf account') + log.debug('Creating zeroconf account') app.config.add_per('accounts', app.ZEROCONF_ACC_NAME) app.config.set_per('accounts', app.ZEROCONF_ACC_NAME, 'autoconnect', True) @@ -123,7 +125,7 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf): def reconnect(self): # Do not try to reco while we are already trying self.time_to_reconnect = None - app.log.debug('reconnect') + log.debug('reconnect') self.disconnect() self.change_status(self.old_show, self.status) diff --git a/gajim/features_window.py b/gajim/features_window.py index 50ce25bdb..496e15de9 100644 --- a/gajim/features_window.py +++ b/gajim/features_window.py @@ -73,10 +73,6 @@ class FeaturesWindow: _('Spellchecking of composed messages.'), _('Requires libgtkspell.'), _('Requires libgtkspell and libenchant.')), - _('Notification'): (self.notification_available, - _('Passive popups notifying for new events.'), - _('Requires python-notify or instead python-dbus in conjunction with notification-daemon.'), - _('Feature not available under Windows.')), _('Automatic status'): (self.idle_available, _('Ability to measure idle time, in order to set auto status.'), _('Requires libxss library.'), @@ -199,18 +195,6 @@ class FeaturesWindow: return False return True - def notification_available(self): - if os.name == 'nt': - return False - from gajim.common import dbus_support - if self.dbus_available() and dbus_support.get_notifications_interface(): - return True - try: - __import__('pynotify') - except Exception: - return False - return True - def idle_available(self): from gajim.common import sleepy return sleepy.SUPPORTED diff --git a/gajim/gajim.py b/gajim/gajim.py index 905ec6b65..ddaa8e7b3 100644 --- a/gajim/gajim.py +++ b/gajim/gajim.py @@ -314,7 +314,8 @@ class GajimApplication(Gtk.Application): ('-delete-motd', action.on_delete_motd, 'online', 's'), ('-activate-bookmark', action.on_activate_bookmark, 'online', 'a{sv}'), - ('-import-contacts', action.on_import_contacts, 'online', 's') + ('-open-event', action.on_open_event, 'always', 'a{sv}'), + ('-import-contacts', action.on_import_contacts, 'online', 's'), ] # General Stateful Actions @@ -347,7 +348,7 @@ class GajimApplication(Gtk.Application): ('features', action.on_features), ('content', action.on_contents), ('about', action.on_about), - ('faq', action.on_faq) + ('faq', action.on_faq), ] for action in self.general_actions: diff --git a/gajim/groupchat_control.py b/gajim/groupchat_control.py index 92bda930b..39f4121bf 100644 --- a/gajim/groupchat_control.py +++ b/gajim/groupchat_control.py @@ -785,18 +785,18 @@ class GroupchatControl(ChatControlBase): self._show_lock_image(**encryption_state) - def _show_lock_image(self, visible, enc_type='', - authenticated=False): + def _show_lock_image(self, visible, enc_type='', authenticated=False): """ Set lock icon visibility and create tooltip """ if authenticated: authenticated_string = _('and authenticated') - img_path = gtkgui_helpers.get_icon_path('security-high') + self.lock_image.set_from_icon_name( + 'security-high', Gtk.IconSize.MENU) else: authenticated_string = _('and NOT authenticated') - img_path = gtkgui_helpers.get_icon_path('security-low') - self.lock_image.set_from_file(img_path) + self.lock_image.set_from_icon_name( + 'security-low', Gtk.IconSize.MENU) tooltip = _('%(type)s encryption is active %(authenticated)s.') % { 'type': enc_type, 'authenticated': authenticated_string} diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py index 3366898c1..1ef37d818 100644 --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -2309,7 +2309,12 @@ class Interface: app.idlequeue.process() except Exception: # Otherwise, an exception will stop our loop - timeout, in_seconds = app.idlequeue.PROCESS_TIMEOUT + + if sys.platform == 'win32': + timeout, in_seconds = 20, None + else: + timeout, in_seconds = app.idlequeue.PROCESS_TIMEOUT + if in_seconds: GLib.timeout_add_seconds(timeout, self.process_connections) else: @@ -2566,7 +2571,11 @@ class Interface: self.instances['file_transfers'] = dialogs.FileTransfersWindow() GLib.timeout_add(100, self.autoconnect) - timeout, in_seconds = app.idlequeue.PROCESS_TIMEOUT + if sys.platform == 'win32': + timeout, in_seconds = 20, None + else: + timeout, in_seconds = app.idlequeue.PROCESS_TIMEOUT + if in_seconds: GLib.timeout_add_seconds(timeout, self.process_connections) else: diff --git a/gajim/notify.py b/gajim/notify.py index 6322e2898..8be89082f 100644 --- a/gajim/notify.py +++ b/gajim/notify.py @@ -27,31 +27,16 @@ ## along with Gajim. If not, see . ## -import os -import time +import sys from gajim.dialogs import PopupNotificationWindow -from gi.repository import GObject from gi.repository import GLib +from gi.repository import Gio from gajim import gtkgui_helpers from gajim.common import app from gajim.common import helpers from gajim.common import ged -from gajim.common import dbus_support -if dbus_support.supported: - import dbus - - -USER_HAS_PYNOTIFY = True # user has pynotify module -try: - import gi - gi.require_version('Notify', '0.7') - from gi.repository import Notify - Notify.init('Gajim Notification') -except ValueError: - USER_HAS_PYNOTIFY = False - def get_show_in_roster(event, account, contact, session=None): """ Return True if this event must be shown in roster, else False @@ -74,12 +59,11 @@ def get_show_in_systray(event, account, contact, type_=None): return False return app.config.get('trayicon_notification_on_events') -def popup(event_type, jid, account, msg_type='', path_to_image=None, title=None, +def popup(event_type, jid, account, type_='', path_to_image=None, title=None, text=None, timeout=-1): """ - Notify a user of an event. It first tries to a valid implementation of - the Desktop Notification Specification. If that fails, then we fall back to - the older style PopupNotificationWindow method + Notify a user of an event using GNotification and GApplication under linux, + the older style PopupNotificationWindow method under windows """ # default image if not path_to_image: @@ -88,67 +72,44 @@ text=None, timeout=-1): if timeout < 0: timeout = app.config.get('notification_timeout') - # Try to show our popup via D-Bus and notification daemon - if app.config.get('use_notif_daemon') and dbus_support.supported: - try: - DesktopNotification(event_type, jid, account, msg_type, - path_to_image, title, GLib.markup_escape_text(text), timeout) - return # sucessfully did D-Bus Notification procedure! - except dbus.DBusException as e: - # Connection to D-Bus failed - app.log.debug(str(e)) - except TypeError as e: - # This means that we sent the message incorrectly - app.log.debug(str(e)) - - # Ok, that failed. Let's try pynotify, which also uses notification daemon - if app.config.get('use_notif_daemon') and USER_HAS_PYNOTIFY: - if not text and event_type == 'new_message': - # empty text for new_message means do_preview = False - # -> default value for text - _text = GLib.markup_escape_text(app.get_name_from_jid(account, - jid)) - else: - _text = GLib.markup_escape_text(text) - - if not title: - _title = '' - else: - _title = title - - notification = Notify.Notification.new(_title, _text) - notification.set_timeout(timeout*1000) - - notification.set_category(event_type) - notification._data = {} - notification._data["event_type"] = event_type - notification._data["jid"] = jid - notification._data["account"] = account - notification._data["msg_type"] = msg_type - notification.set_property('icon-name', path_to_image) - if 'actions' in Notify.get_server_caps(): - notification.add_action('default', 'Default Action', - on_pynotify_notification_clicked) - - try: - notification.show() - return - except GObject.GError as e: - # Connection to notification-daemon failed, see #2893 - app.log.debug(str(e)) - - # Either nothing succeeded or the user wants old-style notifications - instance = PopupNotificationWindow(event_type, jid, account, msg_type, - path_to_image, title, text, timeout) - app.interface.roster.popup_notification_windows.append(instance) - -def on_pynotify_notification_clicked(notification, action): - jid = notification._data.jid - account = notification._data.account - msg_type = notification._data.msg_type + if sys.platform == 'win32': + instance = PopupNotificationWindow(event_type, jid, account, type_, + path_to_image, title, text, timeout) + app.interface.roster.popup_notification_windows.append(instance) + return + + # use GNotification + # TODO: Move to standard GTK+ icons here. + icon = Gio.FileIcon.new(Gio.File.new_for_path(path_to_image)) + notification = Gio.Notification() + if title is not None: + notification.set_title(title) + if text is not None: + notification.set_body(text) + notification.set_icon(icon) + notif_id = None + if event_type in (_('Contact Signed In'), _('Contact Signed Out'), + _('New Message'), _('New Single Message'), _('New Private Message'), + _('Contact Changed Status'), _('File Transfer Request'), + _('File Transfer Error'), _('File Transfer Completed'), + _('File Transfer Stopped'), _('Groupchat Invitation'), + _('Connection Failed'), _('Subscription request'), _('Unsubscribed')): + # Create Variant Dict + dict_ = {'account': GLib.Variant('s', account), + 'jid': GLib.Variant('s', jid), + 'type_': GLib.Variant('s', type_)} + variant_dict = GLib.Variant('a{sv}', dict_) + action = 'app.{}-open-event'.format(account) + notification.add_button_with_target('Open', action, variant_dict) + notification.set_default_action_and_target(action, variant_dict) + if event_type in (_('New Message'), _('New Single Message'), + _('New Private Message')): + # Only one notification per JID + notif_id = jid + notification.set_priority(Gio.NotificationPriority.NORMAL) + notification.set_urgent(False) + app.app.send_notification(notif_id, notification) - notification.close() - app.interface.handle_event(account, jid, msg_type) class Notification: """ @@ -184,297 +145,3 @@ class Notification: helpers.exec_command(obj.command, use_shell=True) except Exception: pass - -class NotificationResponseManager: - """ - Collect references to pending DesktopNotifications and manages there - signalling. This is necessary due to a bug in DBus where you can't remove a - signal from an interface once it's connected - """ - - def __init__(self): - self.pending = {} - self.received = [] - self.interface = None - - def attach_to_interface(self): - if self.interface is not None: - return - self.interface = dbus_support.get_notifications_interface() - self.interface.connect_to_signal('ActionInvoked', - self.on_action_invoked) - self.interface.connect_to_signal('NotificationClosed', self.on_closed) - - def on_action_invoked(self, id_, reason): - if id_ in self.pending: - notification = self.pending[id_] - notification.on_action_invoked(id_, reason) - del self.pending[id_] - return - # got an action on popup that isn't handled yet? Maybe user clicked too - # fast. Remember it. - self.received.append((id_, time.time(), reason)) - if len(self.received) > 20: - curt = time.time() - for rec in self.received: - diff = curt - rec[1] - if diff > 10: - self.received.remove(rec) - - def on_closed(self, id_, reason=None): - if id_ in self.pending: - del self.pending[id_] - - def add_pending(self, id_, object_): - # Check to make sure that we handle an event immediately if we're adding - # an id that's already been triggered - for rec in self.received: - if rec[0] == id_: - object_.on_action_invoked(id_, rec[2]) - self.received.remove(rec) - return - if id_ not in self.pending: - # Add it - self.pending[id_] = object_ - else: - # We've triggered an event that has a duplicate ID! - app.log.debug('Duplicate ID of notification. Can\'t handle this.') - -notification_response_manager = NotificationResponseManager() - -class DesktopNotification: - """ - A DesktopNotification that interfaces with D-Bus via the Desktop - Notification Specification - """ - - def __init__(self, event_type, jid, account, msg_type='', - path_to_image=None, title=None, text=None, timeout=-1): - self.path_to_image = os.path.abspath(path_to_image) - self.event_type = event_type - self.title = title - self.text = text - self.timeout = timeout - # 0.3.1 is the only version of notification daemon that has no way - # to determine which version it is. If no method exists, it means - # they're using that one. - self.default_version = [0, 3, 1] - self.account = account - self.jid = jid - self.msg_type = msg_type - - # default value of text - if not text and event_type == 'new_message': - # empty text for new_message means do_preview = False - self.text = app.get_name_from_jid(account, jid) - - if not title: - self.title = event_type # default value - - if event_type == _('Contact Signed In'): - ntype = 'presence.online' - elif event_type == _('Contact Signed Out'): - ntype = 'presence.offline' - elif event_type in (_('New Message'), _('New Single Message'), - _('New Private Message')): - ntype = 'im.received' - elif event_type == _('File Transfer Request'): - ntype = 'transfer' - elif event_type == _('File Transfer Error'): - ntype = 'transfer.error' - elif event_type in (_('File Transfer Completed'), - _('File Transfer Stopped')): - ntype = 'transfer.complete' - elif event_type == _('New E-mail'): - ntype = 'email.arrived' - elif event_type == _('Groupchat Invitation'): - ntype = 'im.invitation' - elif event_type == _('Contact Changed Status'): - ntype = 'presence.status' - elif event_type == _('Connection Failed'): - ntype = 'connection.failed' - elif event_type == _('Subscription request'): - ntype = 'subscription.request' - elif event_type == _('Unsubscribed'): - ntype = 'unsubscribed' - else: - # default failsafe values - self.path_to_image = gtkgui_helpers.get_icon_path( - 'gajim-chat_msg_recv', 48) - ntype = 'im' # Notification Type - - self.notif = dbus_support.get_notifications_interface(self) - if self.notif is None: - raise dbus.DBusException('unable to get notifications interface') - self.ntype = ntype - - if self.kde_notifications: - self.attempt_notify() - else: - self.capabilities = self.notif.GetCapabilities() - if self.capabilities is None: - self.capabilities = ['actions'] - self.get_version() - - def attempt_notify(self): - ntype = self.ntype - if self.kde_notifications: - notification_text = ('' \ - '%(title)s
%(text)s') % {'title': self.title, - 'text': self.text, 'image': self.path_to_image} - gajim_icon = gtkgui_helpers.get_icon_path('org.gajim.Gajim', 48) - try: - self.notif.Notify( - dbus.String(_('Gajim')), # app_name (string) - dbus.UInt32(0), # replaces_id (uint) - ntype, # event_id (string) - dbus.String(gajim_icon), # app_icon (string) - dbus.String(''), # summary (string) - dbus.String(notification_text), # body (string) - # actions (stringlist) - (dbus.String('default'), dbus.String(self.event_type), - dbus.String('ignore'), dbus.String(_('Ignore'))), - [], # hints (not used in KDE yet) - dbus.UInt32(self.timeout*1000), # timeout (int), in ms - reply_handler=self.attach_by_id, - error_handler=self.notify_another_way) - return - except Exception: - pass - version = self.version - if version[:2] == [0, 2]: - actions = {} - if 'actions' in self.capabilities and self.msg_type: - actions = {'default': 0} - try: - self.notif.Notify( - dbus.String(_('Gajim')), - dbus.String(self.path_to_image), - dbus.UInt32(0), - ntype, - dbus.Byte(0), - dbus.String(self.title), - dbus.String(self.text), - [dbus.String(self.path_to_image)], - actions, - [''], - True, - dbus.UInt32(self.timeout), - reply_handler=self.attach_by_id, - error_handler=self.notify_another_way) - except AttributeError: - # we're actually dealing with the newer version - version = [0, 3, 1] - if version > [0, 3]: - if app.interface.systray_enabled and \ - app.config.get('attach_notifications_to_systray'): - status_icon = app.interface.systray.status_icon - rect = status_icon.get_geometry()[2] - x, y, width, height = rect.x, rect.y, rect.width, rect.height - pos_x = x + (width / 2) - pos_y = y + (height / 2) - hints = {'x': pos_x, 'y': pos_y} - else: - hints = {} - if version >= [0, 3, 2]: - hints['urgency'] = dbus.Byte(0) # Low Urgency - hints['category'] = dbus.String(ntype) - # it seems notification-daemon doesn't like empty text - if self.text: - text = self.text - if len(self.text) > 200: - text = '%s\n…' % self.text[:200] - else: - text = ' ' - if os.environ.get('KDE_FULL_SESSION') == 'true': - text = '' \ - '
' \ - ' %s
' % (self.path_to_image, - text) - self.path_to_image = os.path.abspath( - gtkgui_helpers.get_icon_path('org.gajim.Gajim', 48)) - actions = () - if 'actions' in self.capabilities and self.msg_type: - actions = (dbus.String('default'), dbus.String( - self.event_type)) - try: - self.notif.Notify( - dbus.String(_('Gajim')), - # this notification does not replace other - dbus.UInt32(0), - dbus.String(self.path_to_image), - dbus.String(self.title), - dbus.String(text), - actions, - hints, - dbus.UInt32(self.timeout*1000), - reply_handler=self.attach_by_id, - error_handler=self.notify_another_way) - except Exception as e: - self.notify_another_way(e) - else: - try: - self.notif.Notify( - dbus.String(_('Gajim')), - dbus.String(self.path_to_image), - dbus.UInt32(0), - dbus.String(self.title), - dbus.String(self.text), - dbus.String(''), - hints, - dbus.UInt32(self.timeout*1000), - reply_handler=self.attach_by_id, - error_handler=self.notify_another_way) - except Exception as e: - self.notify_another_way(e) - - def attach_by_id(self, id_): - notification_response_manager.attach_to_interface() - notification_response_manager.add_pending(id_, self) - - def notify_another_way(self, e): - app.log.debug('Error when trying to use notification daemon: %s' % \ - str(e)) - instance = PopupNotificationWindow(self.event_type, self.jid, - self.account, self.msg_type, self.path_to_image, self.title, - self.text, self.timeout) - app.interface.roster.popup_notification_windows.append(instance) - - def on_action_invoked(self, id_, reason): - if self.notif is None: - return - self.notif.CloseNotification(dbus.UInt32(id_)) - self.notif = None - - if reason == 'ignore': - return - - app.interface.handle_event(self.account, self.jid, self.msg_type) - - def version_reply_handler(self, name, vendor, version, spec_version=None): - if spec_version: - version = spec_version - elif vendor == 'Xfce' and version.startswith('0.1.0'): - version = '0.9' - version_list = version.split('.') - self.version = [] - try: - while len(version_list): - self.version.append(int(version_list.pop(0))) - except ValueError: - self.version_error_handler_3_x_try(None) - self.attempt_notify() - - def get_version(self): - self.notif.GetServerInfo( - reply_handler=self.version_reply_handler, - error_handler=self.version_error_handler_2_x_try) - - def version_error_handler_2_x_try(self, e): - self.notif.GetServerInformation( - reply_handler=self.version_reply_handler, - error_handler=self.version_error_handler_3_x_try) - - def version_error_handler_3_x_try(self, e): - self.version = self.default_version - self.attempt_notify() diff --git a/setup.py b/setup.py index 220112d58..030bb77e2 100755 --- a/setup.py +++ b/setup.py @@ -219,7 +219,7 @@ package_data_moods = ['data/moods/*/*.png'] package_data_other = ['data/other/*'] package_data_sounds = ['data/sounds/*.wav'] package_data_style = ['data/style/gajim.css'] -package_plugins_data = ['plugins/*/*'] +package_plugins_data = ['data/plugins/*/*'] package_data = (package_data_activities + package_data_emoticons + package_data_gui -- 2.20.1