Am Mittwoch, 8. März 2017 21:32:45 UTC+1 schrieb Peter Suter:
>
> What plugins do you use? Can you reproduce with all plugins disabled? Do 
> you have more than one active INotificationDistributor that supports 
> transport email?
>

My_NotificationSubscriber.py
FieldTooltip-0.6-py2.7.egg
SimpleMultiProject-0.5.2.dev0-py2.7.egg
TracAutocompleteUsersPlugin-0.4.4.dev0-py2.7.egg
TracCodeReviewer-1.0.0.dev0-py2.7.egg
TracDiscussion-0.10.0-py2.7.egg
TracDynamicFields-2.2.0.dev0-py2.7.egg
TracFullBlogPlugin-0.1.5-py2.7.egg
TracGanttCalendarPlugin-0.6.4.post0-py2.7.egg
TracHtmlNotificationPlugin-0.12.0.1-py2.7.egg
TracIncludeMacro-3.0.0.dev0-py2.7.egg
Trac_jsGantt-0.11.post0-py2.7.egg
trac_NewTicketLikeThisPlugin-0.2-py2.7.egg
TracQuiet-1.2.0.dev0-py2.7.egg
TracSubTicketsPlugin-0.4.1-py2.7.egg
TracTicketFieldsLayoutPlugin-0.12.0.2-py2.7.egg
TracWikiExtras-1.3.1.dev0-py2.7.egg
TracWorkflowAdmin-0.12.0.3-py2.7.egg
WikiAutoComplete-1.1-py2.7.egg

With this custom notifications in My_NotificationSubscriber.py:

from trac.core import Component, implements
from trac.ticket.api import TicketSystem
from trac.notification.api import INotificationSubscriber, 
NotificationSystem
from trac.notification.mail import RecipientMatcher
from trac.notification.model import Subscription
from trac.web.chrome import Chrome

class AuthorCustomFieldSubscriber(Component):
    """Subscriber for custom ticket field."""

    implements(INotificationSubscriber)
   
    field_name = 'author'

    def matches(self, event):
    if event.realm != 'ticket':
        return
    if event.category not in ('created', 'changed', 'attachment added',
                  'attachment deleted'):
        return

    # Custom field with comma-separated string. Parse to set.
    chrome = Chrome(self.env)
    to_set = lambda field: set(chrome.cc_list(field))
    field_set = to_set(event.target[self.field_name] or '')

    # Harvest previous field values
    if 'fields' in event.changes and self.field_name in event.changes[
'fields']:
        field_set.update(to_set(event.changes['fields'][self.field_name][
'old']))

    matcher = RecipientMatcher(self.env)
    klass = self.__class__.__name__
    sids = set()
    for field in field_set:
        recipient = matcher.match_recipient(field)
        if not recipient:
        continue

        sid, auth, addr = recipient

        # Default subscription
        for s in self.default_subscriptions():
        yield s[0], s[1], sid, auth, addr, s[2], s[3], s[4]
        if sid:
        sids.add((sid, auth))

    for s in Subscription.find_by_sids_and_class(self.env, sids, klass):
        yield s.subscription_tuple()

    def description(self):
    ticket_system = TicketSystem(self.env)
    ticket_field_labels = ticket_system.get_ticket_field_labels()
    field_label = ticket_field_labels[self.field_name]
    return "Ticket that I'm listed in as %s is modified. [custom]" % 
field_label

    def default_subscriptions(self):
    klass = self.__class__.__name__
    return NotificationSystem(self.env).default_subscriptions(klass)

    def requires_authentication(self):
    return True

class ReviewerCustomFieldSubscriber(Component):
    """Subscriber for custom ticket field."""

    implements(INotificationSubscriber)
   
    field_name = 'reviewer'

    def matches(self, event):
    if event.realm != 'ticket':
        return
    if event.category not in ('created', 'changed', 'attachment added',
                  'attachment deleted'):
        return

    # Custom field with comma-separated string. Parse to set.
    chrome = Chrome(self.env)
    to_set = lambda field: set(chrome.cc_list(field))
    field_set = to_set(event.target[self.field_name] or '')

    # Harvest previous field values
    if 'fields' in event.changes and self.field_name in event.changes[
'fields']:
        field_set.update(to_set(event.changes['fields'][self.field_name][
'old']))

    matcher = RecipientMatcher(self.env)
    klass = self.__class__.__name__
    sids = set()
    for field in field_set:
        recipient = matcher.match_recipient(field)
        if not recipient:
        continue

        sid, auth, addr = recipient

        # Default subscription
        for s in self.default_subscriptions():
        yield s[0], s[1], sid, auth, addr, s[2], s[3], s[4]
        if sid:
        sids.add((sid, auth))

    for s in Subscription.find_by_sids_and_class(self.env, sids, klass):
        yield s.subscription_tuple()

    def description(self):
    ticket_system = TicketSystem(self.env)
    ticket_field_labels = ticket_system.get_ticket_field_labels()
    field_label = ticket_field_labels[self.field_name]
    return "Ticket that I'm listed in as %s is modified. [custom]" % 
field_label

    def default_subscriptions(self):
    klass = self.__class__.__name__
    return NotificationSystem(self.env).default_subscriptions(klass)

    def requires_authentication(self):
    return True

class DeveloperCustomFieldSubscriber(Component):
    """Subscriber for custom ticket field."""

    implements(INotificationSubscriber)
   
    field_name = 'developer'

    def matches(self, event):
    if event.realm != 'ticket':
        return
    if event.category not in ('created', 'changed', 'attachment added',
                  'attachment deleted'):
        return

    # Custom field with comma-separated string. Parse to set.
    chrome = Chrome(self.env)
    to_set = lambda field: set(chrome.cc_list(field))
    field_set = to_set(event.target[self.field_name] or '')

    # Harvest previous field values
    if 'fields' in event.changes and self.field_name in event.changes[
'fields']:
        field_set.update(to_set(event.changes['fields'][self.field_name][
'old']))

    matcher = RecipientMatcher(self.env)
    klass = self.__class__.__name__
    sids = set()
    for field in field_set:
        recipient = matcher.match_recipient(field)
        if not recipient:
        continue

        sid, auth, addr = recipient

        # Default subscription
        for s in self.default_subscriptions():
        yield s[0], s[1], sid, auth, addr, s[2], s[3], s[4]
        if sid:
        sids.add((sid, auth))

    for s in Subscription.find_by_sids_and_class(self.env, sids, klass):
        yield s.subscription_tuple()

    def description(self):
    ticket_system = TicketSystem(self.env)
    ticket_field_labels = ticket_system.get_ticket_field_labels()
    field_label = ticket_field_labels[self.field_name]
    return "Ticket that I'm listed in as %s is modified. [custom]" % 
field_label

    def default_subscriptions(self):
    klass = self.__class__.__name__
    return NotificationSystem(self.env).default_subscriptions(klass)

    def requires_authentication(self):
    return True

class TesterCustomFieldSubscriber(Component):
    """Subscriber for custom ticket field."""

    implements(INotificationSubscriber)
   
    field_name = 'tester'

    def matches(self, event):
    if event.realm != 'ticket':
        return
    if event.category not in ('created', 'changed', 'attachment added',
                  'attachment deleted'):
        return

    # Custom field with comma-separated string. Parse to set.
    chrome = Chrome(self.env)
    to_set = lambda field: set(chrome.cc_list(field))
    field_set = to_set(event.target[self.field_name] or '')

    # Harvest previous field values
    if 'fields' in event.changes and self.field_name in event.changes[
'fields']:
        field_set.update(to_set(event.changes['fields'][self.field_name][
'old']))

    matcher = RecipientMatcher(self.env)
    klass = self.__class__.__name__
    sids = set()
    for field in field_set:
        recipient = matcher.match_recipient(field)
        if not recipient:
        continue

        sid, auth, addr = recipient

        # Default subscription
        for s in self.default_subscriptions():
        yield s[0], s[1], sid, auth, addr, s[2], s[3], s[4]
        if sid:
        sids.add((sid, auth))

    for s in Subscription.find_by_sids_and_class(self.env, sids, klass):
        yield s.subscription_tuple()

    def description(self):
    ticket_system = TicketSystem(self.env)
    ticket_field_labels = ticket_system.get_ticket_field_labels()
    field_label = ticket_field_labels[self.field_name]
    return "Ticket that I'm listed in as %s is modified. [custom]" % 
field_label

    def default_subscriptions(self):
    klass = self.__class__.__name__
    return NotificationSystem(self.env).default_subscriptions(klass)

    def requires_authentication(self):
    return True

class IntegratorCustomFieldSubscriber(Component):
    """Subscriber for custom ticket field."""

    implements(INotificationSubscriber)
   
    field_name = 'integrator'

    def matches(self, event):
    if event.realm != 'ticket':
        return
    if event.category not in ('created', 'changed', 'attachment added',
                  'attachment deleted'):
        return

    # Custom field with comma-separated string. Parse to set.
    chrome = Chrome(self.env)
    to_set = lambda field: set(chrome.cc_list(field))
    field_set = to_set(event.target[self.field_name] or '')

    # Harvest previous field values
    if 'fields' in event.changes and self.field_name in event.changes[
'fields']:
        field_set.update(to_set(event.changes['fields'][self.field_name][
'old']))

    matcher = RecipientMatcher(self.env)
    klass = self.__class__.__name__
    sids = set()
    for field in field_set:
        recipient = matcher.match_recipient(field)
        if not recipient:
        continue

        sid, auth, addr = recipient

        # Default subscription
        for s in self.default_subscriptions():
        yield s[0], s[1], sid, auth, addr, s[2], s[3], s[4]
        if sid:
        sids.add((sid, auth))

    for s in Subscription.find_by_sids_and_class(self.env, sids, klass):
        yield s.subscription_tuple()

    def description(self):
    ticket_system = TicketSystem(self.env)
    ticket_field_labels = ticket_system.get_ticket_field_labels()
    field_label = ticket_field_labels[self.field_name]
    return "Ticket that I'm listed in as %s is modified. [custom]" % 
field_label

    def default_subscriptions(self):
    klass = self.__class__.__name__
    return NotificationSystem(self.env).default_subscriptions(klass)

    def requires_authentication(self):
    return True

Can you see anything (including the config) that could trigger duplicate 
emails?

Best regards,
Mo

-- 
You received this message because you are subscribed to the Google Groups "Trac 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/trac-users.
For more options, visit https://groups.google.com/d/optout.

Reply via email to