diff --git a/deluge/event.py b/deluge/event.py
index 67618e9df..9a9017762 100644
--- a/deluge/event.py
+++ b/deluge/event.py
@@ -17,9 +17,9 @@
#
# You should have received a copy of the GNU General Public License
# along with deluge. If not, write to:
-# The Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor
-# Boston, MA 02110-1301, USA.
+# The Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor
+# Boston, MA 02110-1301, USA.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the OpenSSL
@@ -41,15 +41,15 @@ and subsequently emitted to the clients.
"""
-event_list = []
+known_events = {}
class DelugeEventMetaClass(type):
"""
This metaclass simply keeps a list of all events classes created.
"""
def __init__(cls, name, bases, dct):
- event_list.append(name)
super(DelugeEventMetaClass, cls).__init__(name, bases, dct)
+ known_events[name] = cls
class DelugeEvent(object):
"""
diff --git a/deluge/plugins/notifications/notifications/common.py b/deluge/plugins/notifications/notifications/common.py
index 39695fab0..0d35caa63 100644
--- a/deluge/plugins/notifications/notifications/common.py
+++ b/deluge/plugins/notifications/notifications/common.py
@@ -22,9 +22,9 @@
#
# You should have received a copy of the GNU General Public License
# along with deluge. If not, write to:
-# The Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor
-# Boston, MA 02110-1301, USA.
+# The Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor
+# Boston, MA 02110-1301, USA.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the OpenSSL
@@ -39,4 +39,35 @@
def get_resource(filename):
import pkg_resources, os
- return pkg_resources.resource_filename("notifications", os.path.join("data", filename))
+ return pkg_resources.resource_filename("notifications",
+ os.path.join("data", filename))
+
+DEFAULT_PREFS = {
+# Core -------------------------------------------------------------------------
+ "smtp_enabled": False,
+ "smtp_host": "",
+ "smtp_port": 25,
+ "smtp_user": "",
+ "smtp_pass": "",
+ "smtp_from": "",
+ "smtp_tls": False, # SSL or TLS
+ "smtp_recipients": [],
+# GTK UI -----------------------------------------------------------------------
+ # BLINK
+ "blink_enabled": False,
+ # FLASH
+ "flash_enabled": False,
+ # POPUP
+ "popup_enabled": False,
+ # SOUND
+ "sound_enabled": False,
+ "sound_path": "",
+# Subscriptions ----------------------------------------------------------------
+ "subscriptions": {
+ "popup": [],
+ "blink": [],
+ "sound": [],
+ "email": [],
+ }
+
+}
diff --git a/deluge/plugins/notifications/notifications/core.py b/deluge/plugins/notifications/notifications/core.py
index 9a1e09359..da81c328d 100644
--- a/deluge/plugins/notifications/notifications/core.py
+++ b/deluge/plugins/notifications/notifications/core.py
@@ -39,20 +39,16 @@
import smtplib
from twisted.internet import defer, threads
+from deluge.event import known_events, DelugeEvent
from deluge.log import LOG as log
from deluge.plugins.pluginbase import CorePluginBase
import deluge.component as component
import deluge.configmanager
from deluge.core.rpcserver import export
-# Relative imports
-from manager import Notifications
-import events
+from test import TestEmailNotifications
DEFAULT_PREFS = {
- # BLINK
- "blink_enabled": False,
- # EMAIL
"smtp_enabled": False,
"smtp_host": "",
"smtp_port": 25,
@@ -61,29 +57,36 @@ DEFAULT_PREFS = {
"smtp_from": "",
"smtp_tls": False, # SSL or TLS
"smtp_recipients": [],
- # FLASH
- "flash_enabled": False,
- # POPUP
- "popup_enabled": False,
- # SOUND
- "sound_enabled": False,
- "sound_path": ""
+ # Subscriptions
+ "subscriptions": {
+ "email": []
+ }
}
+class Core(CorePluginBase, component.Component):
+ def __init__(self, plugin_name):
+ CorePluginBase.__init__(self, plugin_name)
+ component.Component.__init__(self, "Notifications")
+ self.email_message_providers = {}
+ self.tn = TestEmailNotifications()
-class Core(CorePluginBase, Notifications):
def enable(self):
- Notifications.enable(self)
- self.config = deluge.configmanager.ConfigManager("notifications.conf",
- DEFAULT_PREFS)
+ self.config = deluge.configmanager.ConfigManager(
+ "notifications-core.conf", DEFAULT_PREFS)
component.get("EventManager").register_event_handler(
"TorrentFinishedEvent", self._on_torrent_finished_event
)
log.debug("\n\nENABLING CORE NOTIFICATIONS\n\n")
+ self.tn.enable()
+# import sys
+# print '\n\n', [(n, k.__module__) for n, k in known_events.items()]
+# print [f for f in sys.modules.keys() if f.startswith("deluge.event")]
def disable(self):
- Notifications.disable(self)
+ self.tn.disable()
log.debug("\n\nDISABLING CORE NOTIFICATIONS\n\n")
+ for eventtype in self.email_message_providers.keys():
+ self.deregister_email_message_provider(eventtype)
def update(self):
pass
@@ -100,65 +103,87 @@ class Core(CorePluginBase, Notifications):
"returns the config dictionary"
return self.config.config
- # Notification methods
@export
- def notify_blink(self):
- if not self.config["blink_enabled"]:
- return defer.succeed("Blink notification not enabled")
- return defer.maybeDeferred(
- component.get("EventManager").emit, events.NotificationBlinkEvent())
+ def get_handled_events(self):
+ handled_events = []
+ for evt in sorted(known_events.keys()):
+ if known_events[evt].__module__.startswith('deluge.event'):
+ if evt not in ('TorrentFinishedEvent',):
+ # Skip the base class for all events
+ continue
+ classdoc = known_events[evt].__doc__.strip()
+ handled_events.append((evt, classdoc))
+ log.debug("Handled Notification Events: %s", handled_events)
+ return handled_events
- @export
- def notify_email(self, title='', message='', smtp_from='', recipients=[]):
+ def register_email_message_provider(self, eventtype, handler):
+ """This is used to register email formatters for custom event types.
+
+ :param event: str, the event name
+ :param handler: function, to be called when `:param:event` is emitted
+
+ You're handler should return a tuple of (subject, email_contents).
+ """
+ if eventtype not in known_events:
+ raise Exception("The event \"%s\" is not known" % eventtype)
+ if known_events[eventtype].__module__.startswith('deluge.event'):
+ raise Exception("You cannot register email message providers for "
+ "built-in event types.")
+ if eventtype not in self.email_message_providers:
+ def wrapper(*args, **kwargs):
+ return self._handle_custom_email_message_providers(eventtype,
+ *args,
+ **kwargs)
+ self.email_message_providers[eventtype] = (wrapper, handler)
+ else:
+ wrapper, handler = self.email_message_providers[eventtype]
+ component.get("EventManager").register_event_handler(
+ eventtype, wrapper
+ )
+
+ def deregister_email_message_provider(self, eventtype):
+ wrapper, handler = self.email_message_providers[eventtype]
+ component.get("EventManager").deregister_event_handler(
+ eventtype, wrapper
+ )
+ self.email_message_providers.pop(eventtype)
+
+ def _handle_custom_email_message_providers(self, event, *args, **kwargs):
if not self.config['smtp_enabled']:
- return defer.succeed("SMTP notification not enabled")
- d = threads.deferToThread(self._notify_email, title, message, smtp_from,
- recipients)
- d.addCallback(self._on_notify_sucess, 'email')
- d.addErrback(self._on_notify_failure, 'email')
- return d
+ return defer.succeed("SMTP notification not enabled.")
- @export
- def notify_flash(self, title='', message=''):
- if not self.config["flash_enabled"]:
- return defer.succeed("Flash notification not enabled")
- d = defer.maybeDeferred(component.get("EventManager").emit,
- events.NotificationFlashEvent(title, message))
- d.addCallback(self._on_notify_event_sucess, 'flash')
- d.addErrback(self._on_notify_event_failure, 'flash')
- return d
+ log.debug("\n\nCalling CORE's custom email providers for %s: %s %s",
+ event, args, kwargs)
+ if event in self.config["subscriptions"]["email"]:
+ wrapper, handler = self.email_message_providers[event]
+ log.debug("Found handler: %s", handler)
+ d = defer.maybeDeferred(handler, *args, **kwargs)
+ d.addCallback(self._prepare_email)
+ d.addCallback(self._on_notify_sucess)
+ d.addErrback(self._on_notify_failure)
+ return d
- @export
- def notify_popup(self, title='', message=''):
- if not self.config["popup_enabled"]:
- return defer.succeed("Popup notification not enabled")
- d = defer.maybeDeferred(component.get("EventManager").emit,
- events.NotificationPopupEvent(title, message))
- d.addCallback(self._on_notify_event_sucess, 'popup')
- d.addErrback(self._on_notify_event_failure, 'popup')
- return d
- @export
- def notify_sound(self, sound_path=''):
- if not self.config["sound_enabled"]:
- return defer.succeed("Sound notification not enabled")
- d = defer.maybeDeferred(component.get("EventManager").emit,
- events.NotificationSoundEvent(sound_path))
- d.addCallback(self._on_notify_event_sucess, 'sound')
- d.addErrback(self._on_notify_event_failure, 'sound')
- return d
+ def _prepare_email(self, result):
+ if not self.config['smtp_enabled']:
+ return defer.succeed("SMTP notification not enabled.")
+ subject, message = result
+ log.debug("\n\nSending email with subject: %s: %s", subject, message)
+ return threads.deferToThread(self._notify_email, subject, message)
- def _notify_email(self, title='', message='', smtp_from='', recipients=[]):
+
+ def _notify_email(self, subject='', message=''):
+ log.debug("Email prepared")
config = self.config
- to_addrs = '; '.join(config['smtp_recipients']+recipients)
+ to_addrs = '; '.join(config['smtp_recipients'])
headers = """\
From: %(smtp_from)s
To: %(smtp_recipients)s
-Subject: %(title)s
+Subject: %(subject)s
-""" % {'smtp_from': smtp_from and smtp_from or config['smtp_from'],
- 'title': title,
+""" % {'smtp_from': config['smtp_from'],
+ 'subject': subject,
'smtp_recipients': to_addrs}
message = '\r\n'.join((headers + message).splitlines())
@@ -169,7 +194,7 @@ Subject: %(title)s
err_msg = _("There was an error sending the notification email:"
" %s") % err
log.error(err_msg)
- raise err
+ return err
security_enabled = config['smtp_tls']
@@ -188,12 +213,12 @@ Subject: %(title)s
err_msg = _("The server didn't reply properly to the helo "
"greeting: %s") % err
log.error(err_msg)
- raise err
+ return err
except smtplib.SMTPAuthenticationError, err:
err_msg = _("The server didn't accept the username/password "
"combination: %s") % err
log.error(err_msg)
- raise err
+ return err
try:
try:
@@ -202,7 +227,7 @@ Subject: %(title)s
err_msg = _("There was an error sending the notification email:"
" %s") % err
log.error(err_msg)
- raise err
+ return err
finally:
if security_enabled:
# avoid false failure detection when the server closes
@@ -216,12 +241,13 @@ Subject: %(title)s
server.quit()
return _("Notification email sent.")
+
def _on_torrent_finished_event(self, torrent_id):
- log.debug("\n\nhandler for TorrentFinishedEvent called for CORE")
+ log.debug("\n\nHandler for TorrentFinishedEvent called for CORE")
torrent = component.get("TorrentManager")[torrent_id]
torrent_status = torrent.get_status({})
# Email
- title = _("Finished Torrent \"%(name)s\"") % torrent_status
+ subject = _("Finished Torrent \"%(name)s\"") % torrent_status
message = _(
"This email is to inform you that Deluge has finished "
"downloading \"%(name)s\", which includes %(num_files)i files."
@@ -230,7 +256,17 @@ Subject: %(title)s
"Thank you,\nDeluge."
) % torrent_status
- d = defer.maybeDeferred(self.notify_email, title, message)
- d.addCallback(self._on_notify_sucess, 'email')
- d.addErrback(self._on_notify_failure, 'email')
- log.debug("Email notification callback yielded")
+ d = defer.maybeDeferred(self._prepare_email, [subject, message])
+ d.addCallback(self._on_notify_sucess)
+ d.addErrback(self._on_notify_failure)
+ return d
+
+
+ def _on_notify_sucess(self, result):
+ log.debug("\n\nEMAIL Notification success: %s", result)
+ return result
+
+
+ def _on_notify_failure(self, failure):
+ log.debug("\n\nEMAIL Notification failure: %s", failure)
+ return failure
diff --git a/deluge/plugins/notifications/notifications/data/config.glade b/deluge/plugins/notifications/notifications/data/config.glade
index 1bdd923d3..842b93184 100644
--- a/deluge/plugins/notifications/notifications/data/config.glade
+++ b/deluge/plugins/notifications/notifications/data/config.glade
@@ -8,390 +8,537 @@
True
vertical
-
+
True
- 0
- none
+ queue
-
+
True
- 12
+ vertical
-
+
True
vertical
-
- Blink tray icon
+
True
- True
- False
- True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 0
+ 10
+ 10
+ <b><i><big>Notifications</big></i></b>
+ True
+ False
0
-
+
+
+ 0
+
+
+
+
+ True
+ 0
+ 2
+ 2
+ 2
-
+
True
+ True
-
- Play sound
+
True
- True
- False
- True
-
+ vertical
+
+
+ True
+ 0
+ none
+
+
+ True
+ 12
+
+
+ True
+ vertical
+
+
+ Tray icon blinks enabled
+ True
+ True
+ False
+ True
+
+
+ 0
+
+
+
+
+
+ 1
+
+
+
+
+ True
+
+
+ Sound enabled
+ True
+ True
+ False
+ True
+
+
+
+ False
+ 0
+
+
+
+
+ True
+ False
+
+
+ 2
+ 1
+
+
+
+
+ 2
+
+
+
+
+
+
+
+
+ True
+ 5
+ <b>UI Notifications</b>
+ True
+
+
+ label_item
+
+
+
+
+ 0
+
+
+
+
+ True
+ 0
+ none
+
+
+ True
+ 12
+
+
+ True
+ 7
+ 4
+ 2
+ 2
+
+
+ True
+ Hostname:
+ right
+
+
+ 1
+ 2
+
+
+
+
+ True
+ True
+ ●
+
+
+ 1
+ 2
+ 1
+ 2
+
+
+
+
+ True
+ Port:
+ right
+
+
+ 2
+ 3
+ 1
+ 2
+
+
+
+
+ True
+ True
+ 65535
+ ●
+ 5
+ 25 1 100 1 10 0
+ 1
+ True
+ True
+
+
+ 3
+ 4
+ 1
+ 2
+
+
+
+
+ True
+ Username:
+ right
+
+
+ 2
+ 3
+
+
+
+
+ True
+ True
+ ●
+
+
+ 1
+ 4
+ 2
+ 3
+
+
+
+
+ True
+ Password:
+
+
+ 3
+ 4
+
+
+
+
+ True
+ True
+ False
+ ●
+
+
+ 1
+ 4
+ 3
+ 4
+
+
+
+
+ True
+ 0
+ none
+
+
+ True
+ 12
+
+
+ True
+ 2
+
+
+ True
+ True
+ automatic
+ automatic
+
+
+ True
+ True
+ False
+ horizontal
+
+
+
+
+ 0
+
+
+
+
+ True
+ vertical
+ 5
+ start
+
+
+ gtk-add
+ True
+ True
+ True
+ True
+
+
+
+ False
+ False
+ 0
+
+
+
+
+ gtk-delete
+ True
+ False
+ True
+ True
+ True
+
+
+
+ False
+ False
+ 1
+
+
+
+
+ False
+ 3
+ 1
+
+
+
+
+
+
+
+
+ True
+ <b>Recipients</b>
+ True
+
+
+ label_item
+
+
+
+
+ 4
+ 6
+ 7
+
+
+
+
+ True
+ True
+ False
+ True
+
+
+ True
+ Server requires TLS/SSL
+
+
+
+
+ 1
+ 4
+ 5
+ 6
+ 10
+
+
+
+
+ True
+ From:
+ right
+
+
+ 4
+ 5
+
+
+
+
+ True
+ True
+ ●
+
+
+ 1
+ 4
+ 4
+ 5
+
+
+
+
+ Enabled
+ True
+ True
+ False
+ True
+
+
+
+ 4
+
+
+
+
+
+
+
+
+
+
+
+ True
+ 5
+ <b>Email Notifications</b>
+ True
+
+
+ label_item
+
+
+
+
+ 1
+
+
+
+
+
+
+ True
+ Settings
- False
- 0
+ False
+ tab
-
+
True
- False
+ vertical
+
+
+
+
+
+ True
+ True
+ automatic
+ automatic
+
+
+ True
+ True
+ horizontal
+
+
+
+
+ 1
+
+
+
+
+ True
+ This configuration does not mean that you'll actually receive notifications for all these events.
+ fill
+ True
+
+
+ False
+ 2
+ 2
+
+
- 2
1
+
+
+ True
+ Subscriptions
+
+
+ 1
+ False
+ tab
+
+
+
+
+
+
+
+
+ tab
+
+
-
- 2
-
+
+ 1
+
-
-
- True
- <b>UI Notifications</b>
- True
-
-
- label_item
-
-
0
-
-
- True
- 0
- none
-
-
- True
- 12
-
-
- True
- 7
- 4
- 2
- 2
-
-
- True
- Hostname:
- right
-
-
- 1
- 2
-
-
-
-
- True
- True
- ●
-
-
- 1
- 2
- 1
- 2
-
-
-
-
- True
- Port:
- right
-
-
- 2
- 3
- 1
- 2
-
-
-
-
- True
- True
- 65535
- ●
- 5
- 25 1 100 1 10 10
- 1
- True
- True
-
-
- 3
- 4
- 1
- 2
-
-
-
-
- True
- Username:
- right
-
-
- 2
- 3
-
-
-
-
- True
- True
- ●
-
-
- 1
- 4
- 2
- 3
-
-
-
-
- True
- Password:
-
-
- 3
- 4
-
-
-
-
- True
- True
- False
- ●
-
-
- 1
- 4
- 3
- 4
-
-
-
-
- True
- 0
- none
-
-
- True
- 12
-
-
- True
- 2
-
-
- True
- True
- automatic
- automatic
-
-
- True
- True
- False
- horizontal
-
-
-
-
- 0
-
-
-
-
- True
- vertical
- 5
- start
-
-
- gtk-add
- True
- True
- True
- True
-
-
-
- False
- False
- 0
-
-
-
-
- gtk-delete
- True
- False
- True
- True
- True
-
-
-
- False
- False
- 1
-
-
-
-
- False
- 3
- 1
-
-
-
-
-
-
-
-
- True
- <b>Recipients</b>
- True
-
-
- label_item
-
-
-
-
- 4
- 6
- 7
-
-
-
-
- True
- True
- False
- True
-
-
- True
- Server requires TLS/SSL
-
-
-
-
- 1
- 4
- 5
- 6
- 10
-
-
-
-
- True
- From:
- right
-
-
- 4
- 5
-
-
-
-
- True
- True
- ●
-
-
- 1
- 4
- 4
- 5
-
-
-
-
- Enabled
- True
- True
- False
- True
-
-
-
- 4
-
-
-
-
-
-
-
-
-
-
-
- True
- <b>Email Notifications</b>
- True
-
-
- label_item
-
-
-
-
- 1
-
-
diff --git a/deluge/plugins/notifications/notifications/gtkui.py b/deluge/plugins/notifications/notifications/gtkui.py
index 8fdbdde0f..35c276f80 100644
--- a/deluge/plugins/notifications/notifications/gtkui.py
+++ b/deluge/plugins/notifications/notifications/gtkui.py
@@ -40,11 +40,13 @@
import gtk
from twisted.internet import defer
+from deluge.event import known_events, DelugeEvent
from deluge.log import LOG as log
from deluge.ui.client import client
from deluge.plugins.pluginbase import GtkPluginBase
import deluge.component as component
import deluge.common
+import deluge.configmanager
try:
import pygame
@@ -60,44 +62,148 @@ except ImportError:
# Relative imports
from common import get_resource
-from manager import Notifications
+
+DEFAULT_PREFS = {
+ # BLINK
+ "blink_enabled": False,
+ # FLASH
+ "flash_enabled": False,
+ # POPUP
+ "popup_enabled": False,
+ # SOUND
+ "sound_enabled": False,
+ "sound_path": "",
+ # Subscriptions
+ "subscriptions": {
+ "popup": [],
+ "blink": [],
+ "sound": [],
+ }
+}
RECIPIENT_FIELD, RECIPIENT_EDIT = range(2)
+(SUB_EVENT, SUB_EVENT_DOC, SUB_NOT_EMAIL, SUB_NOT_POPUP, SUB_NOT_BLINK,
+ SUB_NOT_SOUND) = range(6)
+
+
+class GtkUI(GtkPluginBase, component.Component):
+ def __init__(self, plugin_name):
+ GtkPluginBase.__init__(self, plugin_name)
+ component.Component.__init__(self, "Notifications")
-class GtkUI(GtkPluginBase, Notifications):
def enable(self):
- Notifications.enable(self)
+ self.config = deluge.configmanager.ConfigManager(
+ "notifications-gtk.conf", DEFAULT_PREFS
+ )
self.glade = gtk.glade.XML(get_resource("config.glade"))
self.glade.get_widget("smtp_port").set_value(25)
self.prefs = self.glade.get_widget("prefs_box")
self.prefs.show_all()
- self.treeview = self.glade.get_widget("smtp_recipients")
- treeview_selection = self.treeview.get_selection()
- treeview_selection.connect("changed", self.on_treeview_selection_changed)
- self.model = gtk.ListStore(str, bool)
+
+ # SMTP Recipients treeview/model
+ self.recipients_treeview = self.glade.get_widget("smtp_recipients")
+ treeview_selection = self.recipients_treeview.get_selection()
+ treeview_selection.connect(
+ "changed", self.on_recipients_treeview_selection_changed
+ )
+ self.recipients_model = gtk.ListStore(str, bool)
renderer = gtk.CellRendererText()
- renderer.connect("edited", self.on_cell_edited, self.model)
+ renderer.connect("edited", self.on_cell_edited, self.recipients_model)
renderer.set_data("recipient", RECIPIENT_FIELD)
column = gtk.TreeViewColumn("Recipients", renderer,
text=RECIPIENT_FIELD,
editable=RECIPIENT_EDIT)
column.set_expand(True)
- self.treeview.append_column(column)
- self.treeview.set_model(self.model)
+ self.recipients_treeview.append_column(column)
+ self.recipients_treeview.set_model(self.recipients_model)
- deluge.common.get_default_download_dir()
+ # Notification Subscriptions treeview/model
+ self.subscriptions_treeview = self.glade.get_widget("subscriptions_treeview")
+ subscriptions_selection = self.subscriptions_treeview.get_selection()
+ subscriptions_selection.connect(
+ "changed", self.on_subscriptions_treeview_selection_changed
+ )
+ self.subscriptions_treeview.set_tooltip_column(SUB_EVENT_DOC)
+ self.subscriptions_model = gtk.ListStore(str, str, bool, bool, bool, bool)
+
+ renderer = gtk.CellRendererText()
+ renderer.set_data("event", SUB_EVENT)
+ column = gtk.TreeViewColumn("Event", renderer, text=SUB_EVENT)
+ column.set_expand(True)
+ self.subscriptions_treeview.append_column(column)
+
+ renderer = gtk.CellRendererText()
+ renderer.set_data("event_doc", SUB_EVENT)
+ column = gtk.TreeViewColumn("Doc", renderer, text=SUB_EVENT_DOC)
+ column.set_property('visible', False)
+ self.subscriptions_treeview.append_column(column)
+
+
+ renderer = gtk.CellRendererToggle()
+ renderer.set_property('activatable', True)
+ renderer.connect('toggled', self._on_email_col_toggled)
+ column = gtk.TreeViewColumn("Email", renderer, active=SUB_NOT_EMAIL)
+ column.set_clickable(True)
+# column.add_attribute(renderer, "active", False)
+# column.set_expand(True)
+ self.subscriptions_treeview.append_column(column)
+
+ renderer = gtk.CellRendererToggle()
+# renderer.connect("edited", self.on_cell_edited, self.recipients_model)
+# renderer.set_data("popup", SUB_NOT_POPUP)
+ renderer.set_property('activatable', True)
+ renderer.connect( 'toggled', self._on_popup_col_toggled)
+ column = gtk.TreeViewColumn("Popup", renderer, active=SUB_NOT_POPUP)
+ column.set_clickable(True)
+# column.add_attribute(renderer, "active", False)
+# column.set_expand(True)
+ self.subscriptions_treeview.append_column(column)
+
+ renderer = gtk.CellRendererToggle()
+# renderer.connect("edited", self.on_cell_edited, self.recipients_model)
+# renderer.set_data("blink", SUB_NOT_BLINK)
+ renderer.set_property('activatable', True)
+ renderer.connect( 'toggled', self._on_blink_col_toggled)
+ column = gtk.TreeViewColumn("Blink", renderer, active=SUB_NOT_BLINK)
+ column.set_clickable(True)
+# column.add_attribute(renderer, "active", False)
+# column.set_expand(True)
+ self.subscriptions_treeview.append_column(column)
+
+ renderer = gtk.CellRendererToggle()
+# renderer.connect("edited", self.on_cell_edited, self.recipients_model)
+ renderer.set_property('activatable', True)
+ renderer.connect('toggled', self._on_sound_col_toggled)
+# renderer.set_data("sound", SUB_NOT_SOUND)
+ column = gtk.TreeViewColumn("Sound", renderer, active=SUB_NOT_SOUND)
+ column.set_clickable(True)
+# column.add_attribute(renderer, "active", False)
+# column.set_expand(True)
+ self.subscriptions_treeview.append_column(column)
+ self.subscriptions_treeview.set_model(self.subscriptions_model)
+
+ client.notifications.get_handled_events().addCallback(
+ self.popuplate_subscriptions
+ )
self.glade.signal_autoconnect({
'on_add_button_clicked': (self.on_add_button_clicked,
- self.treeview),
+ self.recipients_treeview),
'on_delete_button_clicked': (self.on_delete_button_clicked,
- self.treeview),
+ self.recipients_treeview),
'on_enabled_toggled': self.on_enabled_toggled,
'on_sound_enabled_toggled': self.on_sound_enabled_toggled
})
- component.get("Preferences").add_page("Notifications", self.prefs)
+# component.get("Preferences").add_page("Notifications", self.prefs)
+ prefs = component.get("Preferences")
+ parent = self.prefs.get_parent()
+ if parent:
+ parent.remove(self.prefs)
+ index = prefs.notebook.append_page(self.prefs)
+ prefs.liststore.append([index, "Notifications"])
+
component.get("PluginManager").register_hook("on_apply_prefs",
self.on_apply_prefs)
component.get("PluginManager").register_hook("on_show_prefs",
@@ -106,38 +212,93 @@ class GtkUI(GtkPluginBase, Notifications):
if not POPUP_AVAILABLE:
self.glade.get_widget("popup_enabled").set_property('sensitive',
False)
- else:
- client.register_event_handler("NotificationPopupEvent",
- self.notify_popup)
-
- client.register_event_handler("NotificationBlinkEvent",
- self.notify_blink)
-
- self.tray = component.get("SystemTray")
if not SOUND_AVAILABLE:
self.glade.get_widget("sound_enabled").set_property('sensitive',
False)
self.glade.get_widget('sound_path').set_property('sensitive', False)
- else:
- client.register_event_handler("NotificationSoundEvent",
- self.notify_sound)
+
+ self.systray = component.get("SystemTray")
+ if not hasattr(self.systray, 'tray'):
+ # Tray is not beeing used
+ self.glade.get_widget('blink_enabled').set_property('sensitive',
+ False)
+
client.register_event_handler("TorrentFinishedEvent",
self._on_torrent_finished_event)
- # Force config populate
- client.notifications.get_config().addCallback(self.cb_get_config)
def disable(self):
- Notifications.disable(self)
component.get("Preferences").remove_page("Notifications")
component.get("PluginManager").deregister_hook("on_apply_prefs",
self.on_apply_prefs)
component.get("PluginManager").deregister_hook("on_show_prefs",
self.on_show_prefs)
+ def popuplate_subscriptions(self, handled_events, email_subscriptions=[]):
+ subscriptions_dict = self.config['subscriptions']
+ self.subscriptions_model.clear()
+# self.handled_events = handled_events
+ for event_name, event_doc in handled_events:
+ self.subscriptions_model.set(
+ self.subscriptions_model.append(),
+ SUB_EVENT, event_name,
+ SUB_EVENT_DOC, event_doc,
+ SUB_NOT_EMAIL, event_name in email_subscriptions,
+ SUB_NOT_POPUP, event_name in subscriptions_dict["popup"],
+ SUB_NOT_BLINK, event_name in subscriptions_dict['blink'],
+ SUB_NOT_SOUND, event_name in subscriptions_dict['sound']
+ )
+
+
def on_apply_prefs(self):
log.debug("applying prefs for Notifications")
- config = {
+
+ current_popup_subscriptions = []
+ current_blink_subscriptions = []
+ current_sound_subscriptions = []
+ current_email_subscriptions = []
+ for event, doc, email, popup, blink, sound in self.subscriptions_model:
+ if email:
+ current_email_subscriptions.append(event)
+ if popup:
+ current_popup_subscriptions.append(event)
+ if blink:
+ current_blink_subscriptions.append(event)
+ if sound:
+ current_sound_subscriptions.append(event)
+# saved_popup_subscriptions = self.config['subscriptions']['popup']
+# saved_blink_subscriptions = self.config['subscriptions']['blink']
+# saved_sound_subscriptions = self.config['subscriptions']['sound']
+# for event in current_popup_subscriptions:
+# saved_popup_subscriptions.remove(event)
+# for event in saved_blink_subscriptions:
+# # De register
+# pass
+# for event in current_blink_subscriptions:
+# saved_blink_subscriptions.remove(event)
+# for event in saved_blink_subscriptions:
+# # De register
+# pass
+# for event in current_sound_subscriptions:
+# saved_sound_subscriptions.remove(event)
+# for event in saved_sound_subscriptions:
+# # De register
+# pass
+
+ self.config.config.update({
+ "popup_enabled": self.glade.get_widget("popup_enabled").get_active(),
+ "blink_enabled": self.glade.get_widget("blink_enabled").get_active(),
+ "sound_enabled": self.glade.get_widget("sound_enabled").get_active(),
+ "sound_path": self.glade.get_widget("sound_path").get_filename(),
+ "subscriptions": {
+ "popup": current_popup_subscriptions,
+ "blink": current_blink_subscriptions,
+ "sound": current_sound_subscriptions
+ }
+ })
+ self.config.save()
+
+ core_config = {
"smtp_enabled": self.glade.get_widget("smtp_enabled").get_active(),
"smtp_host": self.glade.get_widget("smtp_host").get_text(),
"smtp_port": self.glade.get_widget("smtp_port").get_value(),
@@ -145,45 +306,42 @@ class GtkUI(GtkPluginBase, Notifications):
"smtp_pass": self.glade.get_widget("smtp_pass").get_text(),
"smtp_from": self.glade.get_widget("smtp_from").get_text(),
"smtp_tls": self.glade.get_widget("smtp_tls").get_active(),
- "smtp_recipients": [dest[0] for dest in self.model if
+ "smtp_recipients": [dest[0] for dest in self.recipients_model if
dest[0]!='USER@HOST'],
- "blink_enabled": self.glade.get_widget("blink_enabled").get_active(),
- "sound_enabled": self.glade.get_widget("sound_enabled").get_active(),
- "sound_path": self.glade.get_widget("sound_path").get_filename(),
- "popup_enabled": self.glade.get_widget("popup_enabled").get_active()
+ "subscriptions": {"email": current_email_subscriptions}
}
-
- client.notifications.set_config(config)
+ client.notifications.set_config(core_config)
def on_show_prefs(self):
client.notifications.get_config().addCallback(self.cb_get_config)
- def cb_get_config(self, config):
+ def cb_get_config(self, core_config):
"callback for on show_prefs"
- self.config = config
- self.glade.get_widget("smtp_host").set_text(config["smtp_host"])
- self.glade.get_widget("smtp_port").set_value(config["smtp_port"])
- self.glade.get_widget("smtp_user").set_text(config["smtp_user"])
- self.glade.get_widget("smtp_pass").set_text(config["smtp_pass"])
- self.glade.get_widget("smtp_from").set_text(config["smtp_from"])
- self.glade.get_widget("smtp_tls").set_active(config["smtp_tls"])
- self.model.clear()
- for recipient in config['smtp_recipients']:
- self.model.set(self.model.append(),
- RECIPIENT_FIELD, recipient,
- RECIPIENT_EDIT, False)
- self.glade.get_widget("smtp_enabled").set_active(config['smtp_enabled'])
+ self.glade.get_widget("smtp_host").set_text(core_config["smtp_host"])
+ self.glade.get_widget("smtp_port").set_value(core_config["smtp_port"])
+ self.glade.get_widget("smtp_user").set_text(core_config["smtp_user"])
+ self.glade.get_widget("smtp_pass").set_text(core_config["smtp_pass"])
+ self.glade.get_widget("smtp_from").set_text(core_config["smtp_from"])
+ self.glade.get_widget("smtp_tls").set_active(core_config["smtp_tls"])
+ self.recipients_model.clear()
+ for recipient in core_config['smtp_recipients']:
+ self.recipients_model.set(self.recipients_model.append(),
+ RECIPIENT_FIELD, recipient,
+ RECIPIENT_EDIT, False)
+ self.glade.get_widget("smtp_enabled").set_active(
+ core_config['smtp_enabled']
+ )
self.glade.get_widget("sound_enabled").set_active(
- config['sound_enabled']
+ self.config['sound_enabled']
)
self.glade.get_widget("popup_enabled").set_active(
- config['popup_enabled']
+ self.config['popup_enabled']
)
self.glade.get_widget("blink_enabled").set_active(
- config['blink_enabled']
+ self.config['blink_enabled']
)
- if config['sound_path']:
- sound_path = config['sound_path']
+ if self.config['sound_path']:
+ sound_path = self.config['sound_path']
else:
sound_path = deluge.common.get_default_download_dir()
self.glade.get_widget("sound_path").set_filename(sound_path)
@@ -191,6 +349,11 @@ class GtkUI(GtkPluginBase, Notifications):
self.on_enabled_toggled(self.glade.get_widget("smtp_enabled"))
self.on_sound_enabled_toggled(self.glade.get_widget('sound_enabled'))
+ client.notifications.get_handled_events().addCallback(
+ self.popuplate_subscriptions, core_config['subscriptions']['email']
+ )
+
+
def on_add_button_clicked(self, widget, treeview):
model = treeview.get_model()
model.set(model.append(),
@@ -210,7 +373,7 @@ class GtkUI(GtkPluginBase, Notifications):
path = model.get_path(iter)[0]
model.set(iter, RECIPIENT_FIELD, new_text)
- def on_treeview_selection_changed(self, selection):
+ def on_recipients_treeview_selection_changed(self, selection):
model, selected_connection_iter = selection.get_selected()
if selected_connection_iter:
self.glade.get_widget("delete_button").set_property('sensitive',
@@ -218,6 +381,16 @@ class GtkUI(GtkPluginBase, Notifications):
else:
self.glade.get_widget("delete_button").set_property('sensitive',
False)
+
+ def on_subscriptions_treeview_selection_changed(self, selection):
+ model, selected_connection_iter = selection.get_selected()
+ if selected_connection_iter:
+ self.glade.get_widget("delete_button").set_property('sensitive',
+ True)
+ else:
+ self.glade.get_widget("delete_button").set_property('sensitive',
+ False)
+
def on_enabled_toggled(self, widget):
if widget.get_active():
for widget in ('smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass',
@@ -237,24 +410,14 @@ class GtkUI(GtkPluginBase, Notifications):
else:
self.glade.get_widget('sound_path').set_property('sensitive', False)
- def notify_blink(self):
- defer.maybeDeferred(self.tray.blink, True)
- return defer.succeed("blink notification shown.")
-
- def notify_email(self, title='', message='', smtp_from='', recipients=[]):
- d = client.notifications.notify_email(title, message, smtp_from,
- recipients)
- d.addCallback(self._on_notify_sucess, "email")
- d.addCallback(self._on_notify_failure, "email")
+ # Notification methods
+ def blink(self):
+ d = defer.maybeDeferred(self.systray.blink, True)
+ d.addCallback(self._on_notify_sucess, "blink")
+ d.addCallback(self._on_notify_failure, "blink")
return d
- def notify_flash(self, title='', message=''):
- d = client.notifications.notify_flash(title, message)
- d.addCallback(self._on_notify_sucess, "flash")
- d.addCallback(self._on_notify_failure, "flash")
- return d
-
- def notify_popup(self, title='', message=''):
+ def popup(self, title='', message=''):
if not self.config['popup_enabled']:
return defer.succeed(_("Popup notification is not enabled."))
if not POPUP_AVAILABLE:
@@ -271,7 +434,7 @@ class GtkUI(GtkPluginBase, Notifications):
return defer.fail(err_msg)
return defer.succeed(_("Notification popup shown"))
- def notify_sound(self, sound_path=''):
+ def play_sound(self, sound_path=''):
if not self.config['sound_enabled']:
return defer.succeed(_("Sound notification not enabled"))
if not SOUND_AVAILABLE:
@@ -295,15 +458,16 @@ class GtkUI(GtkPluginBase, Notifications):
log.info(msg)
return defer.succeed(msg)
+ # Internal methods
def _on_torrent_finished_event(self, torrent_id):
log.debug("\n\nhandler for TorrentFinishedEvent GTKUI called")
# Blink
- d0 = defer.maybeDeferred(self.notify_blink)
+ d0 = defer.maybeDeferred(self.blink)
d0.addCallback(self._on_notify_sucess, 'blink')
d0.addErrback(self._on_notify_failure, 'blink')
log.debug("Blink notification callback yielded")
# Sound
- d1 = defer.maybeDeferred(self.notify_sound)
+ d1 = defer.maybeDeferred(self.play_sound)
d1.addCallback(self._on_notify_sucess, 'sound')
d1.addErrback(self._on_notify_failure, 'sound')
log.debug("Sound notification callback yielded")
@@ -311,6 +475,7 @@ class GtkUI(GtkPluginBase, Notifications):
d2 = client.core.get_torrent_status(torrent_id, ["name", "num_files"])
d2.addCallback(self._on_torrent_finished_event_got_torrent_status)
d2.addErrback(self._on_torrent_finished_event_torrent_status_failure)
+ return defer.succeed("\n\nGtkUI on torrent finished")
def _on_torrent_finished_event_torrent_status_failure(self, failure):
log.debug("Failed to get torrent status to be able to show the popup")
@@ -320,8 +485,36 @@ class GtkUI(GtkPluginBase, Notifications):
title = _("Finished Torrent")
message = _("The torrent \"%(name)s\" including %(num_files)i "
"has finished downloading.") % torrent_status
- d2 = defer.maybeDeferred(self.notify_popup, title, message)
- d2.addCallback(self._on_notify_sucess, 'popup')
- d2.addErrback(self._on_notify_failure, 'popup')
- log.debug("Popup notification callback yielded")
+ d = defer.maybeDeferred(self.popup, title, message)
+ d.addCallback(self._on_notify_sucess, 'popup')
+ d.addErrback(self._on_notify_failure, 'popup')
+ return d
+
+ def _on_notify_sucess(self, result, kind):
+ log.debug("\n\nNotification success using %s: %s", kind, result)
+ return result
+
+ def _on_notify_failure(self, failure, kind):
+ log.debug("\n\nNotification failure using %s: %s", kind, failure)
+ return failure
+
+ def _on_email_col_toggled(self, cell, path):
+ self.subscriptions_model[path][SUB_NOT_EMAIL] = \
+ not self.subscriptions_model[path][SUB_NOT_EMAIL]
+ return
+
+ def _on_popup_col_toggled(self, cell, path):
+ self.subscriptions_model[path][SUB_NOT_POPUP] = \
+ not self.subscriptions_model[path][SUB_NOT_POPUP]
+ return
+
+ def _on_blink_col_toggled(self, cell, path):
+ self.subscriptions_model[path][SUB_NOT_BLINK] = \
+ not self.subscriptions_model[path][SUB_NOT_BLINK]
+ return
+
+ def _on_sound_col_toggled(self, cell, path):
+ self.subscriptions_model[path][SUB_NOT_SOUND] = \
+ not self.subscriptions_model[path][SUB_NOT_SOUND]
+ return
diff --git a/deluge/plugins/notifications/notifications/webui.py b/deluge/plugins/notifications/notifications/webui.py
index 3464ece8f..0a3e07a5b 100644
--- a/deluge/plugins/notifications/notifications/webui.py
+++ b/deluge/plugins/notifications/notifications/webui.py
@@ -37,21 +37,49 @@
# statement from all source files in the program, then also delete it here.
#
+from twisted.internet import defer
from deluge.log import LOG as log
from deluge.ui.client import client
from deluge import component
from deluge.plugins.pluginbase import WebPluginBase
+import deluge.configmanager
# Relative imports
from common import get_resource
-from manager import Notifications
-class WebUI(WebPluginBase, Notifications):
+DEFAULT_PREFS = {
+ # FLASH
+ "flash_enabled": False,
+ # Subscriptions
+ "subscriptions": {
+ "flash": []
+ }
+}
+
+class WebUI(WebPluginBase, component.Component):
scripts = [get_resource("notifications.js")]
+ def __init__(self, plugin_name):
+ WebPluginBase.__init__(self, plugin_name)
+ component.Component.__init__(self, "Notifications")
+
def enable(self):
- Notifications.enable(self)
+ self.config = deluge.configmanager.ConfigManager(
+ "notifications-web.conf", DEFAULT_PREFS
+ )
+ log.debug("Enabling Web UI notifications")
def disable(self):
- Notifications.disable(self)
+ log.debug("Disabling Web UI notifications")
+
+ def flash(self, title, message):
+ return defer.succeed("Web Flash Notifications not implemented yet")
+
+ def _on_notify_sucess(self, result, kind):
+ log.debug("\n\nNotification success using %s: %s", kind, result)
+ return result
+
+ def _on_notify_failure(self, failure, kind):
+ log.debug("\n\nNotification failure using %s: %s", kind, failure)
+ return failure