324 | | See also: #12728 |
| 324 | See also: #12728 |
| 325 | |
| 326 | == Subscribe via custom ticket field |
| 327 | |
| 328 | [TracTicketsCustomFields Custom ticket fields] can be configured to behave like an additional [wiki:TracTickets#/Cc Cc field]. For example a `tester` field can notify employees responsible for testing a feature, separately from other Cc'd users. |
| 329 | |
| 330 | 1. Create a [TracTicketsCustomFields custom ticket fields] `tester`. |
| 331 | 1. Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.notification.api.INotificationSubscriber INotificationSubscriber]: |
| 332 | {{{#!python |
| 333 | # -*- coding: utf-8 -*- |
| 334 | # |
| 335 | # Copyright (C) 2018 Edgewall Software |
| 336 | # All rights reserved. |
| 337 | # |
| 338 | # This software is licensed as described in the file COPYING, which |
| 339 | # you should have received as part of this distribution. The terms |
| 340 | # are also available at http://trac.edgewall.org/wiki/TracLicense. |
| 341 | # |
| 342 | # This software consists of voluntary contributions made by many |
| 343 | # individuals. For the exact contribution history, see the revision |
| 344 | # history and logs, available at http://trac.edgewall.org/log/. |
| 345 | |
| 346 | from trac.core import Component, implements |
| 347 | from trac.notification.api import INotificationSubscriber, NotificationSystem |
| 348 | from trac.notification.mail import RecipientMatcher |
| 349 | from trac.notification.model import Subscription |
| 350 | from trac.web.chrome import Chrome |
| 351 | |
| 352 | class TesterCustomFieldSubscriber(Component): |
| 353 | """Subscriber for custom ticket field.""" |
| 354 | |
| 355 | implements(INotificationSubscriber) |
| 356 | |
| 357 | field_name = 'tester' |
| 358 | |
| 359 | # INotificationSubscriber methods |
| 360 | |
| 361 | def matches(self, event): |
| 362 | if event.realm != 'ticket': |
| 363 | return |
| 364 | if event.category not in ('created', 'changed', 'attachment added', |
| 365 | 'attachment deleted'): |
| 366 | return |
| 367 | |
| 368 | # Custom field with comma-separated string. Parse to set. |
| 369 | chrome = Chrome(self.env) |
| 370 | to_set = lambda field: set(chrome.cc_list(field)) |
| 371 | user_set = to_set(event.target[self.field_name] or '') |
| 372 | |
| 373 | # Harvest previous field values |
| 374 | if 'fields' in event.changes and self.field_name in event.changes['fields']: |
| 375 | field_set.update(to_set(event.changes['fields'][self.field_name]['old'])) |
| 376 | |
| 377 | matcher = RecipientMatcher(self.env) |
| 378 | klass = self.__class__.__name__ |
| 379 | sids = set() |
| 380 | for field in field_set: |
| 381 | recipient = matcher.match_recipient(field) |
| 382 | if not recipient: |
| 383 | continue |
| 384 | sid, auth, addr = recipient |
| 385 | |
| 386 | # Default subscription |
| 387 | for s in self.default_subscriptions(): |
| 388 | yield s[0], s[1], sid, auth, addr, s[2], s[3], s[4] |
| 389 | if sid: |
| 390 | sids.add((sid, auth)) |
| 391 | |
| 392 | for s in Subscription.find_by_sids_and_class(self.env, sids, klass): |
| 393 | yield s.subscription_tuple() |
| 394 | |
| 395 | def description(self): |
| 396 | return "Ticket that I'm listed in as %s is modified" % self.field_name |
| 397 | |
| 398 | def default_subscriptions(self): |
| 399 | klass = self.__class__.__name__ |
| 400 | return NotificationSystem(self.env).default_subscriptions(klass) |
| 401 | |
| 402 | def requires_authentication(self): |
| 403 | return True |
| 404 | }}} |
| 405 | 1. Users can manage this subscription in [/prefs/notification their preferences]. |