Checkpoint:

* Custom notification providers working for email.
  * Configuration through the GtkUI working.
This commit is contained in:
Pedro Algarvio 2009-11-22 02:35:02 +00:00
parent 8a6ec7232d
commit 1b7a50f88b
6 changed files with 939 additions and 504 deletions

View File

@ -17,9 +17,9 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with deluge. If not, write to: # along with deluge. If not, write to:
# The Free Software Foundation, Inc., # The Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor # 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA. # Boston, MA 02110-1301, USA.
# #
# In addition, as a special exception, the copyright holders give # In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the OpenSSL # 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): class DelugeEventMetaClass(type):
""" """
This metaclass simply keeps a list of all events classes created. This metaclass simply keeps a list of all events classes created.
""" """
def __init__(cls, name, bases, dct): def __init__(cls, name, bases, dct):
event_list.append(name)
super(DelugeEventMetaClass, cls).__init__(name, bases, dct) super(DelugeEventMetaClass, cls).__init__(name, bases, dct)
known_events[name] = cls
class DelugeEvent(object): class DelugeEvent(object):
""" """

View File

@ -22,9 +22,9 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with deluge. If not, write to: # along with deluge. If not, write to:
# The Free Software Foundation, Inc., # The Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor # 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA. # Boston, MA 02110-1301, USA.
# #
# In addition, as a special exception, the copyright holders give # In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the OpenSSL # permission to link the code of portions of this program with the OpenSSL
@ -39,4 +39,35 @@
def get_resource(filename): def get_resource(filename):
import pkg_resources, os 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": [],
}
}

View File

@ -39,20 +39,16 @@
import smtplib import smtplib
from twisted.internet import defer, threads from twisted.internet import defer, threads
from deluge.event import known_events, DelugeEvent
from deluge.log import LOG as log from deluge.log import LOG as log
from deluge.plugins.pluginbase import CorePluginBase from deluge.plugins.pluginbase import CorePluginBase
import deluge.component as component import deluge.component as component
import deluge.configmanager import deluge.configmanager
from deluge.core.rpcserver import export from deluge.core.rpcserver import export
# Relative imports from test import TestEmailNotifications
from manager import Notifications
import events
DEFAULT_PREFS = { DEFAULT_PREFS = {
# BLINK
"blink_enabled": False,
# EMAIL
"smtp_enabled": False, "smtp_enabled": False,
"smtp_host": "", "smtp_host": "",
"smtp_port": 25, "smtp_port": 25,
@ -61,29 +57,36 @@ DEFAULT_PREFS = {
"smtp_from": "", "smtp_from": "",
"smtp_tls": False, # SSL or TLS "smtp_tls": False, # SSL or TLS
"smtp_recipients": [], "smtp_recipients": [],
# FLASH # Subscriptions
"flash_enabled": False, "subscriptions": {
# POPUP "email": []
"popup_enabled": False, }
# SOUND
"sound_enabled": False,
"sound_path": ""
} }
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): def enable(self):
Notifications.enable(self) self.config = deluge.configmanager.ConfigManager(
self.config = deluge.configmanager.ConfigManager("notifications.conf", "notifications-core.conf", DEFAULT_PREFS)
DEFAULT_PREFS)
component.get("EventManager").register_event_handler( component.get("EventManager").register_event_handler(
"TorrentFinishedEvent", self._on_torrent_finished_event "TorrentFinishedEvent", self._on_torrent_finished_event
) )
log.debug("\n\nENABLING CORE NOTIFICATIONS\n\n") 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): def disable(self):
Notifications.disable(self) self.tn.disable()
log.debug("\n\nDISABLING CORE NOTIFICATIONS\n\n") 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): def update(self):
pass pass
@ -100,65 +103,87 @@ class Core(CorePluginBase, Notifications):
"returns the config dictionary" "returns the config dictionary"
return self.config.config return self.config.config
# Notification methods
@export @export
def notify_blink(self): def get_handled_events(self):
if not self.config["blink_enabled"]: handled_events = []
return defer.succeed("Blink notification not enabled") for evt in sorted(known_events.keys()):
return defer.maybeDeferred( if known_events[evt].__module__.startswith('deluge.event'):
component.get("EventManager").emit, events.NotificationBlinkEvent()) 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 register_email_message_provider(self, eventtype, handler):
def notify_email(self, title='', message='', smtp_from='', recipients=[]): """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']: if not self.config['smtp_enabled']:
return defer.succeed("SMTP notification not 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
@export log.debug("\n\nCalling CORE's custom email providers for %s: %s %s",
def notify_flash(self, title='', message=''): event, args, kwargs)
if not self.config["flash_enabled"]: if event in self.config["subscriptions"]["email"]:
return defer.succeed("Flash notification not enabled") wrapper, handler = self.email_message_providers[event]
d = defer.maybeDeferred(component.get("EventManager").emit, log.debug("Found handler: %s", handler)
events.NotificationFlashEvent(title, message)) d = defer.maybeDeferred(handler, *args, **kwargs)
d.addCallback(self._on_notify_event_sucess, 'flash') d.addCallback(self._prepare_email)
d.addErrback(self._on_notify_event_failure, 'flash') d.addCallback(self._on_notify_sucess)
return d 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 _prepare_email(self, result):
def notify_sound(self, sound_path=''): if not self.config['smtp_enabled']:
if not self.config["sound_enabled"]: return defer.succeed("SMTP notification not enabled.")
return defer.succeed("Sound notification not enabled") subject, message = result
d = defer.maybeDeferred(component.get("EventManager").emit, log.debug("\n\nSending email with subject: %s: %s", subject, message)
events.NotificationSoundEvent(sound_path)) return threads.deferToThread(self._notify_email, subject, message)
d.addCallback(self._on_notify_event_sucess, 'sound')
d.addErrback(self._on_notify_event_failure, 'sound')
return d
def _notify_email(self, title='', message='', smtp_from='', recipients=[]):
def _notify_email(self, subject='', message=''):
log.debug("Email prepared")
config = self.config config = self.config
to_addrs = '; '.join(config['smtp_recipients']+recipients) to_addrs = '; '.join(config['smtp_recipients'])
headers = """\ headers = """\
From: %(smtp_from)s From: %(smtp_from)s
To: %(smtp_recipients)s To: %(smtp_recipients)s
Subject: %(title)s Subject: %(subject)s
""" % {'smtp_from': smtp_from and smtp_from or config['smtp_from'], """ % {'smtp_from': config['smtp_from'],
'title': title, 'subject': subject,
'smtp_recipients': to_addrs} 'smtp_recipients': to_addrs}
message = '\r\n'.join((headers + message).splitlines()) message = '\r\n'.join((headers + message).splitlines())
@ -169,7 +194,7 @@ Subject: %(title)s
err_msg = _("There was an error sending the notification email:" err_msg = _("There was an error sending the notification email:"
" %s") % err " %s") % err
log.error(err_msg) log.error(err_msg)
raise err return err
security_enabled = config['smtp_tls'] security_enabled = config['smtp_tls']
@ -188,12 +213,12 @@ Subject: %(title)s
err_msg = _("The server didn't reply properly to the helo " err_msg = _("The server didn't reply properly to the helo "
"greeting: %s") % err "greeting: %s") % err
log.error(err_msg) log.error(err_msg)
raise err return err
except smtplib.SMTPAuthenticationError, err: except smtplib.SMTPAuthenticationError, err:
err_msg = _("The server didn't accept the username/password " err_msg = _("The server didn't accept the username/password "
"combination: %s") % err "combination: %s") % err
log.error(err_msg) log.error(err_msg)
raise err return err
try: try:
try: try:
@ -202,7 +227,7 @@ Subject: %(title)s
err_msg = _("There was an error sending the notification email:" err_msg = _("There was an error sending the notification email:"
" %s") % err " %s") % err
log.error(err_msg) log.error(err_msg)
raise err return err
finally: finally:
if security_enabled: if security_enabled:
# avoid false failure detection when the server closes # avoid false failure detection when the server closes
@ -216,12 +241,13 @@ Subject: %(title)s
server.quit() server.quit()
return _("Notification email sent.") return _("Notification email sent.")
def _on_torrent_finished_event(self, torrent_id): 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 = component.get("TorrentManager")[torrent_id]
torrent_status = torrent.get_status({}) torrent_status = torrent.get_status({})
# Email # Email
title = _("Finished Torrent \"%(name)s\"") % torrent_status subject = _("Finished Torrent \"%(name)s\"") % torrent_status
message = _( message = _(
"This email is to inform you that Deluge has finished " "This email is to inform you that Deluge has finished "
"downloading \"%(name)s\", which includes %(num_files)i files." "downloading \"%(name)s\", which includes %(num_files)i files."
@ -230,7 +256,17 @@ Subject: %(title)s
"Thank you,\nDeluge." "Thank you,\nDeluge."
) % torrent_status ) % torrent_status
d = defer.maybeDeferred(self.notify_email, title, message) d = defer.maybeDeferred(self._prepare_email, [subject, message])
d.addCallback(self._on_notify_sucess, 'email') d.addCallback(self._on_notify_sucess)
d.addErrback(self._on_notify_failure, 'email') d.addErrback(self._on_notify_failure)
log.debug("Email notification callback yielded") 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

View File

@ -8,390 +8,537 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<widget class="GtkFrame" id="frame2"> <widget class="GtkViewport" id="viewport1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="label_xalign">0</property> <property name="resize_mode">queue</property>
<property name="shadow_type">none</property>
<child> <child>
<widget class="GtkAlignment" id="alignment2"> <widget class="GtkVBox" id="vbox4">
<property name="visible">True</property> <property name="visible">True</property>
<property name="left_padding">12</property> <property name="orientation">vertical</property>
<child> <child>
<widget class="GtkVBox" id="vbox1"> <widget class="GtkVBox" id="vbox3">
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<widget class="GtkCheckButton" id="blink_enabled"> <widget class="GtkLabel" id="label22">
<property name="label" translatable="yes">Blink tray icon</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="receives_default">False</property> <property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="xpad">10</property>
<property name="ypad">10</property>
<property name="label" translatable="yes">&lt;b&gt;&lt;i&gt;&lt;big&gt;Notifications&lt;/big&gt;&lt;/i&gt;&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget> </widget>
<packing> <packing>
<property name="expand">False</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child> <child>
<widget class="GtkCheckButton" id="popup_enabled"> <widget class="GtkHSeparator" id="hseparator1">
<property name="label" translatable="yes">Show popup</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</widget> </widget>
<packing> <packing>
<property name="expand">False</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="yalign">0</property>
<property name="bottom_padding">2</property>
<property name="left_padding">2</property>
<property name="right_padding">2</property>
<child> <child>
<widget class="GtkHBox" id="hbox2"> <widget class="GtkNotebook" id="notebook1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property>
<child> <child>
<widget class="GtkCheckButton" id="sound_enabled"> <widget class="GtkVBox" id="vbox2">
<property name="label" translatable="yes">Play sound</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="orientation">vertical</property>
<property name="receives_default">False</property> <child>
<property name="draw_indicator">True</property> <widget class="GtkFrame" id="frame2">
<signal name="toggled" handler="on_sound_enabled_toggled"/> <property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkCheckButton" id="blink_enabled">
<property name="label" translatable="yes">Tray icon blinks enabled</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="popup_enabled">
<property name="label" translatable="yes">Popups enabled</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="sound_enabled">
<property name="label" translatable="yes">Sound enabled</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_sound_enabled_toggled"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkFileChooserButton" id="sound_path">
<property name="visible">True</property>
<property name="create_folders">False</property>
</widget>
<packing>
<property name="padding">2</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="xpad">5</property>
<property name="label" translatable="yes">&lt;b&gt;UI Notifications&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkTable" id="prefs_table">
<property name="visible">True</property>
<property name="n_rows">7</property>
<property name="n_columns">4</property>
<property name="column_spacing">2</property>
<property name="row_spacing">2</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Hostname:</property>
<property name="justify">right</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="smtp_host">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">Port:</property>
<property name="justify">right</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkSpinButton" id="smtp_port">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">65535</property>
<property name="invisible_char">&#x25CF;</property>
<property name="width_chars">5</property>
<property name="adjustment">25 1 100 1 10 0</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">Username:</property>
<property name="justify">right</property>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="smtp_user">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes">Password:</property>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="smtp_pass">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="invisible_char">&#x25CF;</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<widget class="GtkTreeView" id="smtp_recipients">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="enable_grid_lines">horizontal</property>
</widget>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkVButtonBox" id="vbuttonbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<property name="layout_style">start</property>
<child>
<widget class="GtkButton" id="add_button">
<property name="label">gtk-add</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_add_button_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="delete_button">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_delete_button_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">3</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Recipients&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="right_attach">4</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="smtp_tls">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<child>
<widget class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="label" translatable="yes">Server requires TLS/SSL</property>
</widget>
</child>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_padding">10</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="label" translatable="yes">From:</property>
<property name="justify">right</property>
</widget>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="smtp_from">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="smtp_enabled">
<property name="label" translatable="yes">Enabled</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_enabled_toggled"/>
</widget>
<packing>
<property name="right_attach">4</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="xpad">5</property>
<property name="label" translatable="yes">&lt;b&gt;Email Notifications&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="label" translatable="yes">Settings</property>
</widget> </widget>
<packing> <packing>
<property name="expand">False</property> <property name="tab_fill">False</property>
<property name="position">0</property> <property name="type">tab</property>
</packing> </packing>
</child> </child>
<child> <child>
<widget class="GtkFileChooserButton" id="sound_path"> <widget class="GtkVBox" id="vbox5">
<property name="visible">True</property> <property name="visible">True</property>
<property name="create_folders">False</property> <property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<widget class="GtkTreeView" id="subscriptions_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="enable_grid_lines">horizontal</property>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="label" translatable="yes">This configuration does not mean that you'll actually receive notifications for all these events.</property>
<property name="justify">fill</property>
<property name="wrap">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">2</property>
<property name="position">2</property>
</packing>
</child>
</widget> </widget>
<packing> <packing>
<property name="padding">2</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<widget class="GtkLabel" id="label11">
<property name="visible">True</property>
<property name="label" translatable="yes">Subscriptions</property>
</widget>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
<packing>
<property name="type">tab</property>
</packing>
</child>
</widget> </widget>
<packing>
<property name="position">2</property>
</packing>
</child> </child>
</widget> </widget>
<packing>
<property name="position">1</property>
</packing>
</child> </child>
</widget> </widget>
</child> </child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;UI Notifications&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget> </widget>
<packing> <packing>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child>
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkTable" id="prefs_table">
<property name="visible">True</property>
<property name="n_rows">7</property>
<property name="n_columns">4</property>
<property name="column_spacing">2</property>
<property name="row_spacing">2</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Hostname:</property>
<property name="justify">right</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="smtp_host">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">Port:</property>
<property name="justify">right</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkSpinButton" id="smtp_port">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">65535</property>
<property name="invisible_char">&#x25CF;</property>
<property name="width_chars">5</property>
<property name="adjustment">25 1 100 1 10 10</property>
<property name="climb_rate">1</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">Username:</property>
<property name="justify">right</property>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="smtp_user">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes">Password:</property>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="smtp_pass">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="invisible_char">&#x25CF;</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<widget class="GtkTreeView" id="smtp_recipients">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="enable_grid_lines">horizontal</property>
</widget>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkVButtonBox" id="vbuttonbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<property name="layout_style">start</property>
<child>
<widget class="GtkButton" id="add_button">
<property name="label">gtk-add</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_add_button_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="delete_button">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_delete_button_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">3</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Recipients&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="right_attach">4</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="smtp_tls">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<child>
<widget class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="label" translatable="yes">Server requires TLS/SSL</property>
</widget>
</child>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_padding">10</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="label" translatable="yes">From:</property>
<property name="justify">right</property>
</widget>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="smtp_from">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="smtp_enabled">
<property name="label" translatable="yes">Enabled</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_enabled_toggled"/>
</widget>
<packing>
<property name="right_attach">4</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Email Notifications&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget> </widget>
</child> </child>
</widget> </widget>

View File

@ -40,11 +40,13 @@
import gtk import gtk
from twisted.internet import defer from twisted.internet import defer
from deluge.event import known_events, DelugeEvent
from deluge.log import LOG as log from deluge.log import LOG as log
from deluge.ui.client import client from deluge.ui.client import client
from deluge.plugins.pluginbase import GtkPluginBase from deluge.plugins.pluginbase import GtkPluginBase
import deluge.component as component import deluge.component as component
import deluge.common import deluge.common
import deluge.configmanager
try: try:
import pygame import pygame
@ -60,44 +62,148 @@ except ImportError:
# Relative imports # Relative imports
from common import get_resource 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) 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): 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 = gtk.glade.XML(get_resource("config.glade"))
self.glade.get_widget("smtp_port").set_value(25) self.glade.get_widget("smtp_port").set_value(25)
self.prefs = self.glade.get_widget("prefs_box") self.prefs = self.glade.get_widget("prefs_box")
self.prefs.show_all() self.prefs.show_all()
self.treeview = self.glade.get_widget("smtp_recipients")
treeview_selection = self.treeview.get_selection() # SMTP Recipients treeview/model
treeview_selection.connect("changed", self.on_treeview_selection_changed) self.recipients_treeview = self.glade.get_widget("smtp_recipients")
self.model = gtk.ListStore(str, bool) 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 = 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) renderer.set_data("recipient", RECIPIENT_FIELD)
column = gtk.TreeViewColumn("Recipients", renderer, column = gtk.TreeViewColumn("Recipients", renderer,
text=RECIPIENT_FIELD, text=RECIPIENT_FIELD,
editable=RECIPIENT_EDIT) editable=RECIPIENT_EDIT)
column.set_expand(True) column.set_expand(True)
self.treeview.append_column(column) self.recipients_treeview.append_column(column)
self.treeview.set_model(self.model) 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({ self.glade.signal_autoconnect({
'on_add_button_clicked': (self.on_add_button_clicked, 'on_add_button_clicked': (self.on_add_button_clicked,
self.treeview), self.recipients_treeview),
'on_delete_button_clicked': (self.on_delete_button_clicked, 'on_delete_button_clicked': (self.on_delete_button_clicked,
self.treeview), self.recipients_treeview),
'on_enabled_toggled': self.on_enabled_toggled, 'on_enabled_toggled': self.on_enabled_toggled,
'on_sound_enabled_toggled': self.on_sound_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", component.get("PluginManager").register_hook("on_apply_prefs",
self.on_apply_prefs) self.on_apply_prefs)
component.get("PluginManager").register_hook("on_show_prefs", component.get("PluginManager").register_hook("on_show_prefs",
@ -106,38 +212,93 @@ class GtkUI(GtkPluginBase, Notifications):
if not POPUP_AVAILABLE: if not POPUP_AVAILABLE:
self.glade.get_widget("popup_enabled").set_property('sensitive', self.glade.get_widget("popup_enabled").set_property('sensitive',
False) 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: if not SOUND_AVAILABLE:
self.glade.get_widget("sound_enabled").set_property('sensitive', self.glade.get_widget("sound_enabled").set_property('sensitive',
False) False)
self.glade.get_widget('sound_path').set_property('sensitive', False) self.glade.get_widget('sound_path').set_property('sensitive', False)
else:
client.register_event_handler("NotificationSoundEvent", self.systray = component.get("SystemTray")
self.notify_sound) 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", client.register_event_handler("TorrentFinishedEvent",
self._on_torrent_finished_event) self._on_torrent_finished_event)
# Force config populate
client.notifications.get_config().addCallback(self.cb_get_config)
def disable(self): def disable(self):
Notifications.disable(self)
component.get("Preferences").remove_page("Notifications") component.get("Preferences").remove_page("Notifications")
component.get("PluginManager").deregister_hook("on_apply_prefs", component.get("PluginManager").deregister_hook("on_apply_prefs",
self.on_apply_prefs) self.on_apply_prefs)
component.get("PluginManager").deregister_hook("on_show_prefs", component.get("PluginManager").deregister_hook("on_show_prefs",
self.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): def on_apply_prefs(self):
log.debug("applying prefs for Notifications") 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_enabled": self.glade.get_widget("smtp_enabled").get_active(),
"smtp_host": self.glade.get_widget("smtp_host").get_text(), "smtp_host": self.glade.get_widget("smtp_host").get_text(),
"smtp_port": self.glade.get_widget("smtp_port").get_value(), "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_pass": self.glade.get_widget("smtp_pass").get_text(),
"smtp_from": self.glade.get_widget("smtp_from").get_text(), "smtp_from": self.glade.get_widget("smtp_from").get_text(),
"smtp_tls": self.glade.get_widget("smtp_tls").get_active(), "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'], dest[0]!='USER@HOST'],
"blink_enabled": self.glade.get_widget("blink_enabled").get_active(), "subscriptions": {"email": current_email_subscriptions}
"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()
} }
client.notifications.set_config(core_config)
client.notifications.set_config(config)
def on_show_prefs(self): def on_show_prefs(self):
client.notifications.get_config().addCallback(self.cb_get_config) 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" "callback for on show_prefs"
self.config = config self.glade.get_widget("smtp_host").set_text(core_config["smtp_host"])
self.glade.get_widget("smtp_host").set_text(config["smtp_host"]) self.glade.get_widget("smtp_port").set_value(core_config["smtp_port"])
self.glade.get_widget("smtp_port").set_value(config["smtp_port"]) self.glade.get_widget("smtp_user").set_text(core_config["smtp_user"])
self.glade.get_widget("smtp_user").set_text(config["smtp_user"]) self.glade.get_widget("smtp_pass").set_text(core_config["smtp_pass"])
self.glade.get_widget("smtp_pass").set_text(config["smtp_pass"]) self.glade.get_widget("smtp_from").set_text(core_config["smtp_from"])
self.glade.get_widget("smtp_from").set_text(config["smtp_from"]) self.glade.get_widget("smtp_tls").set_active(core_config["smtp_tls"])
self.glade.get_widget("smtp_tls").set_active(config["smtp_tls"]) self.recipients_model.clear()
self.model.clear() for recipient in core_config['smtp_recipients']:
for recipient in config['smtp_recipients']: self.recipients_model.set(self.recipients_model.append(),
self.model.set(self.model.append(), RECIPIENT_FIELD, recipient,
RECIPIENT_FIELD, recipient, RECIPIENT_EDIT, False)
RECIPIENT_EDIT, False) self.glade.get_widget("smtp_enabled").set_active(
self.glade.get_widget("smtp_enabled").set_active(config['smtp_enabled']) core_config['smtp_enabled']
)
self.glade.get_widget("sound_enabled").set_active( self.glade.get_widget("sound_enabled").set_active(
config['sound_enabled'] self.config['sound_enabled']
) )
self.glade.get_widget("popup_enabled").set_active( self.glade.get_widget("popup_enabled").set_active(
config['popup_enabled'] self.config['popup_enabled']
) )
self.glade.get_widget("blink_enabled").set_active( self.glade.get_widget("blink_enabled").set_active(
config['blink_enabled'] self.config['blink_enabled']
) )
if config['sound_path']: if self.config['sound_path']:
sound_path = config['sound_path'] sound_path = self.config['sound_path']
else: else:
sound_path = deluge.common.get_default_download_dir() sound_path = deluge.common.get_default_download_dir()
self.glade.get_widget("sound_path").set_filename(sound_path) 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_enabled_toggled(self.glade.get_widget("smtp_enabled"))
self.on_sound_enabled_toggled(self.glade.get_widget('sound_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): def on_add_button_clicked(self, widget, treeview):
model = treeview.get_model() model = treeview.get_model()
model.set(model.append(), model.set(model.append(),
@ -210,7 +373,7 @@ class GtkUI(GtkPluginBase, Notifications):
path = model.get_path(iter)[0] path = model.get_path(iter)[0]
model.set(iter, RECIPIENT_FIELD, new_text) 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() model, selected_connection_iter = selection.get_selected()
if selected_connection_iter: if selected_connection_iter:
self.glade.get_widget("delete_button").set_property('sensitive', self.glade.get_widget("delete_button").set_property('sensitive',
@ -218,6 +381,16 @@ class GtkUI(GtkPluginBase, Notifications):
else: else:
self.glade.get_widget("delete_button").set_property('sensitive', self.glade.get_widget("delete_button").set_property('sensitive',
False) 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): def on_enabled_toggled(self, widget):
if widget.get_active(): if widget.get_active():
for widget in ('smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass', for widget in ('smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass',
@ -237,24 +410,14 @@ class GtkUI(GtkPluginBase, Notifications):
else: else:
self.glade.get_widget('sound_path').set_property('sensitive', False) self.glade.get_widget('sound_path').set_property('sensitive', False)
def notify_blink(self): # Notification methods
defer.maybeDeferred(self.tray.blink, True) def blink(self):
return defer.succeed("blink notification shown.") d = defer.maybeDeferred(self.systray.blink, True)
d.addCallback(self._on_notify_sucess, "blink")
def notify_email(self, title='', message='', smtp_from='', recipients=[]): d.addCallback(self._on_notify_failure, "blink")
d = client.notifications.notify_email(title, message, smtp_from,
recipients)
d.addCallback(self._on_notify_sucess, "email")
d.addCallback(self._on_notify_failure, "email")
return d return d
def notify_flash(self, title='', message=''): def popup(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=''):
if not self.config['popup_enabled']: if not self.config['popup_enabled']:
return defer.succeed(_("Popup notification is not enabled.")) return defer.succeed(_("Popup notification is not enabled."))
if not POPUP_AVAILABLE: if not POPUP_AVAILABLE:
@ -271,7 +434,7 @@ class GtkUI(GtkPluginBase, Notifications):
return defer.fail(err_msg) return defer.fail(err_msg)
return defer.succeed(_("Notification popup shown")) return defer.succeed(_("Notification popup shown"))
def notify_sound(self, sound_path=''): def play_sound(self, sound_path=''):
if not self.config['sound_enabled']: if not self.config['sound_enabled']:
return defer.succeed(_("Sound notification not enabled")) return defer.succeed(_("Sound notification not enabled"))
if not SOUND_AVAILABLE: if not SOUND_AVAILABLE:
@ -295,15 +458,16 @@ class GtkUI(GtkPluginBase, Notifications):
log.info(msg) log.info(msg)
return defer.succeed(msg) return defer.succeed(msg)
# Internal methods
def _on_torrent_finished_event(self, torrent_id): def _on_torrent_finished_event(self, torrent_id):
log.debug("\n\nhandler for TorrentFinishedEvent GTKUI called") log.debug("\n\nhandler for TorrentFinishedEvent GTKUI called")
# Blink # Blink
d0 = defer.maybeDeferred(self.notify_blink) d0 = defer.maybeDeferred(self.blink)
d0.addCallback(self._on_notify_sucess, 'blink') d0.addCallback(self._on_notify_sucess, 'blink')
d0.addErrback(self._on_notify_failure, 'blink') d0.addErrback(self._on_notify_failure, 'blink')
log.debug("Blink notification callback yielded") log.debug("Blink notification callback yielded")
# Sound # Sound
d1 = defer.maybeDeferred(self.notify_sound) d1 = defer.maybeDeferred(self.play_sound)
d1.addCallback(self._on_notify_sucess, 'sound') d1.addCallback(self._on_notify_sucess, 'sound')
d1.addErrback(self._on_notify_failure, 'sound') d1.addErrback(self._on_notify_failure, 'sound')
log.debug("Sound notification callback yielded") 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 = client.core.get_torrent_status(torrent_id, ["name", "num_files"])
d2.addCallback(self._on_torrent_finished_event_got_torrent_status) d2.addCallback(self._on_torrent_finished_event_got_torrent_status)
d2.addErrback(self._on_torrent_finished_event_torrent_status_failure) 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): def _on_torrent_finished_event_torrent_status_failure(self, failure):
log.debug("Failed to get torrent status to be able to show the popup") 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") title = _("Finished Torrent")
message = _("The torrent \"%(name)s\" including %(num_files)i " message = _("The torrent \"%(name)s\" including %(num_files)i "
"has finished downloading.") % torrent_status "has finished downloading.") % torrent_status
d2 = defer.maybeDeferred(self.notify_popup, title, message) d = defer.maybeDeferred(self.popup, title, message)
d2.addCallback(self._on_notify_sucess, 'popup') d.addCallback(self._on_notify_sucess, 'popup')
d2.addErrback(self._on_notify_failure, 'popup') d.addErrback(self._on_notify_failure, 'popup')
log.debug("Popup notification callback yielded") 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

View File

@ -37,21 +37,49 @@
# statement from all source files in the program, then also delete it here. # 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.log import LOG as log
from deluge.ui.client import client from deluge.ui.client import client
from deluge import component from deluge import component
from deluge.plugins.pluginbase import WebPluginBase from deluge.plugins.pluginbase import WebPluginBase
import deluge.configmanager
# Relative imports # Relative imports
from common import get_resource 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")] scripts = [get_resource("notifications.js")]
def __init__(self, plugin_name):
WebPluginBase.__init__(self, plugin_name)
component.Component.__init__(self, "Notifications")
def enable(self): 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): 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