| 1 | == Extension Point : ''INotificationSubscriber'' == |
| 2 | |
| 3 | ||'''Interface'''||''INotificationSubscriber''||'''Since'''||[wiki:TracDev/ApiChanges/1.1.2#INotificationSubscriber 1.1.2]|| |
| 4 | ||'''Module'''||''trac.notification''||'''Source'''||[source:psuter/trac/notification/api.py@advanced-notification-subscriptions#/INotificationSubscriber api.py]|| |
| 5 | |
| 6 | The ''INotificationSubscriber'' subscribes users to [TracNotification notification] events. |
| 7 | |
| 8 | == Purpose == |
| 9 | |
| 10 | Trac provides an extendible and flexible notification system. Different people are interested in different kinds of notifications. Notification subscriptions allow administrators and / or users to configure the exact rules used that trigger sending of notifications. |
| 11 | |
| 12 | == Usage == |
| 13 | |
| 14 | Implementing the interface follows the standard guidelines found in [wiki:TracDev/ComponentArchitecture] and of course [wiki:TracDev/PluginDevelopment]. |
| 15 | |
| 16 | The main part of this interface is the `match()` function. It returns a list of subscriptions, in the form of tuples consisting of: |
| 17 | * `class`: The name of the Python class. (This could probably be removed.) |
| 18 | * `distributor`: Also known as `transport`. E.g. the string `email`. See [wiki:TracDev/Proposals/AdvancedNotification/INotificationDistributor INotificationDistributor]. |
| 19 | * `sid`: The session ID of the subscriber. (Can be `None` if `address` is provided.) |
| 20 | * `authenticated`: `1` for authenticated session IDs, `0` for anonymous session IDs. |
| 21 | * `address`: The (email) address to use. (Can be `None` if `sid` is provided.) |
| 22 | * `format`: The MIME type to be used (e.g. `text/plain` or `text/html`.) |
| 23 | * `priority`: An integer priority. Smaller numbers have higher priority than bigger numbers. `1` is the highest priority. |
| 24 | * `adverb`: Either the string `always` or `never`. |
| 25 | |
| 26 | Since more then one component can handle the same realms and categories, the priorities and adverbs are used to resolve conflicting subscriptions. |
| 27 | |
| 28 | The implementation can use any means to determine if a user is interested in hearing about a given event. |
| 29 | Most check that the appropriate conditions apply and then retrieve the required information from the [wiki:TracDev/Proposals/AdvancedNotification/DatabaseSchema#Tablesubscription subscription] DB table. |
| 30 | |
| 31 | The subscriptions in that table are configured in a shared preferences panel that uses two other methods of this interface: |
| 32 | The simple `description()` method returns a description string shown to the user in the preferences panel (or `None` if the plugin does use the `subscriptions` DB table.) |
| 33 | The `requires_authentication()` method allows hiding the rule from unauthenticated users. (E.g. because only authenticated users can be ticket owners.) |
| 34 | |
| 35 | The `default_subscriptions()` method describes any default subscriptions that automatically exist without the user configuring `subscription` DB entries in the preferences. |
| 36 | These are also displayed on the preferences panel, but can not be directly modified there. (They usually can be overriden by non-default subscriptions.) |
| 37 | The plugin still has to return the respective subscriptions from the `matches()` method. |
| 38 | |
| 39 | Default descriptions should be used when users can be determined by the event itself. |
| 40 | For instance, ticket author has a default subscription that is controlled via trac.ini. |
| 41 | Default subscriptions should be low priority (i.e. have a priority number much larger than `1`, like 100) so that the user can easily override them. |
| 42 | |
| 43 | == Examples == |
| 44 | |
| 45 | The following example implements a simple subscriber that can trigger notifications when a new ticket is created with a high priority level. |
| 46 | {{{#!python |
| 47 | from trac.core import * |
| 48 | from trac.notification.api import INotificationSubscriber |
| 49 | from trac.notification.model import Subscription |
| 50 | |
| 51 | class HighPriorityTicketNotificationSubscriber(Component): |
| 52 | |
| 53 | implements(INotificationSubscriber) |
| 54 | |
| 55 | # INotificationSubscriber methods |
| 56 | |
| 57 | def matches(self, event): |
| 58 | if event.realm != 'ticket': |
| 59 | return |
| 60 | if event.category != 'created': |
| 61 | return |
| 62 | |
| 63 | ticket = event.target |
| 64 | if ticket['priority'] not in ('blocker', 'critical', 'major'): |
| 65 | return |
| 66 | |
| 67 | klass = self.__class__.__name__ |
| 68 | for i in Subscription.find_by_class(self.env, klass): |
| 69 | yield i.subscription_tuple() |
| 70 | |
| 71 | def description(self): |
| 72 | return "notify me when new high priority tickets are created" |
| 73 | |
| 74 | def requires_authentication(self): |
| 75 | return False |
| 76 | }}} |
| 77 | |
| 78 | == Available Implementations == |
| 79 | |
| 80 | Several implementations are part of core Trac: |
| 81 | |
| 82 | * `trac.ticket.notification.AllTicketSubscriber`\\ |
| 83 | Allows anyone to subscribe to all ticket change notifications. |
| 84 | * `trac.ticket.notification.TicketOwnerSubscriber`\\ |
| 85 | Allows ticket owners to subscribe to (or unsubscribe from) change notifications for owned tickets. |
| 86 | * `trac.ticket.notification.TicketComponentOwnerSubscriber`\\ |
| 87 | Allows component owners to subscribe to (or unsubscribe from) change notifications for tickets assigned to owned component. |
| 88 | * `trac.ticket.notification.TicketUpdaterSubscriber`\\ |
| 89 | Allows anyone to subscribe to (or unsubscribe from) change notifications for their own ticket changes. |
| 90 | * `trac.ticket.notification.TicketReporterSubscriber`\\ |
| 91 | Allows ticket reporters to subscribe to (or unsubscribe from) change notifications for tickets they created. |
| 92 | * `trac.ticket.notification.CarbonCopySubscriber`\\ |
| 93 | Allows anyone to subscribe to (or unsubscribe from) change notifications for tickets where they are listed in CC. |
| 94 | |
| 95 | == Additional Information and References == |
| 96 | |
| 97 | * [http://www.edgewall.org/docs/trac-trunk/epydoc/trac.notification.api.INotificationSubscriber-class.html epydoc] |
| 98 | * [http://www.edgewall.org/docs/trac-trunk/html/api/trac_notification.html#trac.notification.api.INotificationSubscriber API Reference] |
| 99 | * The precursor of this interface was `IAnnouncementSubscriber` from the th:AnnouncerPlugin. |
| 100 | * DONE The `IAnnouncementDefaultSubscriber` from the th:AnnouncerPlugin was also folded into this interface. |
| 101 | |
| 102 | == Open Questions |
| 103 | |
| 104 | === Merge `description()` and `requires_authentication()` |
| 105 | These methods are both only used in the preferences panel. |
| 106 | Merging them could be easier to understand and more flexible, allowing different requirement checks: |
| 107 | {{{ |
| 108 | def get_subscription_preference_description(self, req): |
| 109 | if req.session.authenticated and 'TICKET_ADMIN' in req.perm: |
| 110 | return "notify me when an admin is needed" |
| 111 | }}} |
| 112 | |
| 113 | === Remove `class` name |
| 114 | The `class` item could be removed from the tuple returned by `matches()`. It's not really needed and clutters up each plugin with ugly `klass = self.__class__.__name__` lines. |
| 115 | |
| 116 | Counter-arguments: |
| 117 | * It helps debugging and can be logged. |
| 118 | * But if that's needed the `NotificationSystem` should do so, to avoid mistakes and simplify plugins. |
| 119 | * The [wiki:TracDev/Proposals/AdvancedNotification/DatabaseSchema#Tablesubscription subscription] DB table requires that anyway. |
| 120 | * But it should be replaced there as well, e.g. by a freely chosen `rule` string. |