= Custom Notification Subscriptions [[PageOutline(2)]] Subscriptions allow site administrators and users to subscribe to notifications under different conditions. TracNotification customizations can often be implemented with a short plugin. Some examples of custom subscriptions are given on this page. == Subscribe to all ticket notifications The [TracIni#notification-smtp_always_cc-option "[notification] smtp_always_cc"] config option will send ''all notifications'' to the specified address. Without plugins this is the same as ''all ticket notifications'', but some plugins may send non-ticket notifications that you don't want to send to that address. Then it may be useful to have a `[notification] ticket_always_cc` config option that will only send ''all ticket notifications'' to the specified address: 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.notification.api.INotificationSubscriber INotificationSubscriber]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2017 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import Component, implements from trac.notification.api import INotificationSubscriber from trac.notification.mail import RecipientMatcher class TicketAlwaysEmailSubscriber(Component): """Implement a policy to send an email to a certain address for all ticket notifications. Controlled via the ticket_always_cc and smtp_always_bcc option in the notification section of trac.ini. """ implements(INotificationSubscriber) def matches(self, event): if event.realm != 'ticket': return matcher = RecipientMatcher(self.env) klass = self.__class__.__name__ format = 'text/plain' priority = 0 for address in self._get_address_list(): recipient = matcher.match_recipient(address) if recipient: sid, authenticated, address = recipient yield (klass, 'email', sid, authenticated, address, format, priority, 'always') def description(self): return None # not configurable def requires_authentication(self): return False def default_subscriptions(self): return () def _get_address_list(self): section = self.config['notification'] def getlist(name): return section.getlist(name, sep=(',', ' '), keep_empty=False) return set(getlist('ticket_always_cc')) | \ set(getlist('ticket_always_bcc')) }}} 1. Configure the `[notification] ticket_always_cc` (or `..._bcc`) config option. See also: #9148 == Subscribe to new created tickets Some projects / users want to subscribe only to new created ticket notifications: 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.notification.api.INotificationSubscriber INotificationSubscriber]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2017 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import Component, implements from trac.notification.api import INotificationSubscriber from trac.notification.model import Subscription from trac.util.translation import _ class NewTicketSubscriber(Component): """Subscribe to new created tickets.""" implements(INotificationSubscriber) # INotificationSubscriber methods def matches(self, event): if event.realm != 'ticket': return if event.category != 'created': return klass = self.__class__.__name__ for s in Subscription.find_by_class(self.env, klass): yield s.subscription_tuple() def description(self): return _("Any ticket is created") def default_subscriptions(self): return [] def requires_authentication(self): return False }}} 1. Users can manage this subscription in [/prefs/notification their preferences]. See also: #6613 == Unsubscribe from ticket updates without comments Some projects / users don't want to subscribe to ticket notifications if only some ticket properties were changed but no comment was added: 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.notification.api.INotificationSubscriber INotificationSubscriber]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2017 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import Component, implements from trac.notification.api import INotificationSubscriber from trac.notification.model import Subscription from trac.util.translation import _ class TicketPropSubscriber(Component): """Subscribe to (or unsubscribe from) ticket updates without comments.""" implements(INotificationSubscriber) # INotificationSubscriber methods def matches(self, event): if event.realm != 'ticket': return if event.category != 'changed': return if event.comment != '': return klass = self.__class__.__name__ for s in Subscription.find_by_class(self.env, klass): yield s.subscription_tuple() def description(self): return _("Only ticket props are changed") def default_subscriptions(self): return [] def requires_authentication(self): return False }}} 1. Users can manage this subscription in [/prefs/notification their preferences]. Add a rule to `Never notify: Only ticket props are changed` to unsubscribe from ticket notifications where only ticket props are changed. See also: #10326 == Subscribe to ticket changes with blocker severity Some projects / users want to subscribe only to ticket notifications where certain ticket field values are set. For example maybe some users are interested in tickets with ''blocker'' severity: 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.notification.api.INotificationSubscriber INotificationSubscriber]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2017 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import Component, implements from trac.notification.api import INotificationSubscriber from trac.notification.model import Subscription from trac.util.translation import _ class BlockerSeverityTicketSubscriber(Component): """Subscribe to tickets with severity blocker.""" implements(INotificationSubscriber) # INotificationSubscriber methods def matches(self, event): if event.realm != 'ticket': return ticket = event.target if ticket['severity'] != 'blocker': return klass = self.__class__.__name__ for s in Subscription.find_by_class(self.env, klass): yield s.subscription_tuple() def description(self): return _("Ticket with severity 'blocker' is created or modified") def default_subscriptions(self): return [] def requires_authentication(self): return False }}} 1. Users can manage this subscription in [/prefs/notification their preferences]. ==== Variations * Change `'severity'` to `'priority'` or some other ticket field, and / or `'blocker'` to some other value. * Only consider ''changes'' to a specific ticket field by checking for example `event.changes['fields']['severity']['new']`. * Create complex combinations of multiple ticket field with boolean operators `and`, `or`, `not` etc. See also: #11758 == Subscribe to ticket change replies Some users only want to be notified when someone replies to their ticket changes. 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.notification.api.INotificationSubscriber INotificationSubscriber]: {{{#!python # -*- coding: utf-8 -*- # # Copyright (C) 2017 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. from trac.core import Component, implements from trac.notification.api import INotificationSubscriber from trac.notification.mail import RecipientMatcher from trac.notification.model import Subscription from trac.ticket.model import Ticket from trac.util.translation import _ class ReplyToTicketSubscriber(Component): """Subscribe to new created tickets.""" implements(INotificationSubscriber) # INotificationSubscriber methods def matches(self, event): if event.realm != 'ticket': return if event.category != 'changed': return if not event.changes: return replyto_author = self._get_replyto_author(event) matcher = RecipientMatcher(self.env) recipient = matcher.match_recipient(replyto_author) if not recipient: return sid, auth, addr = recipient if sid: klass = self.__class__.__name__ for s in Subscription \ .find_by_sids_and_class(self.env, ((sid, auth),), klass): yield s.subscription_tuple() def description(self): return _("My ticket change is replied to") def default_subscriptions(self): return [] def requires_authentication(self): return False def _get_replyto_author(self, event): if 'comment' in event.changes['fields'] and \ 'old' in event.changes['fields']['comment'] and \ '.' in event.changes['fields']['comment']['old']: old = event.changes['fields']['comment']['old'] replyto_comment = old.split('.', 1)[0] replyto_change = event.target.get_change(replyto_comment) if replyto_change and 'comment' in replyto_change['fields']: return replyto_change['fields']['comment']['author'] }}} 1. Users can manage this subscription in [/prefs/notification their preferences]. See also: #12728