Ticket #5670 (new enhancement)
Opened 5 years ago
Last modified 14 months ago
[patch] Refactoring of general `NotifyEmail` class to make it easier to subclass
| Reported by: | osimons <simon-code@…> | Owned by: | eblot |
|---|---|---|---|
| Priority: | normal | Milestone: | next-major-0.1X |
| Component: | notification | Version: | devel |
| Severity: | normal | Keywords: | |
| Cc: | thijstriemstra | ||
| Release Notes: | |||
| API Changes: | |||
Description
The NotifyEmail class in trac.notification is a great starting-point for any custom email classes.
I have made a simple class that does my special things in notify() and send(), but where 98% of the logic it is left to NotifyEmail and Notify classes to handle the practical things. Among other things I use my custom Email-sending class to send registration confirmations and similar system mail.
However, for any kind of reuse of this class there is one snag: Notification needs to be enabled for the project. So, if you want to send you own mail or have some other module that sends email there is no easy way to do this based on general classes without copy & paste of logic logic from original class (in notify() as it would need to be 'skipped' in the call-chain). My system email sending new passwords based on 'forgotten password' form should not depend on ticket notification being enabled. As it is now I have to trick the class in my notify() by enabling notification, calling NotifyEmail.notify(), and then setting it back at the end… Not pretty, and potentially a source of application misbehavior.
So much for the argument of why. Here is a small patch that extracts the enable-check logic into a method that can be easily overridden for any subclass of NotifyEmail:
Index: trac/trac/notification.py =================================================================== --- trac/trac/notification.py (revision 5803) +++ trac/trac/notification.py (working copy) @@ -209,11 +209,14 @@ self._charset.output_charset = 'ascii' else: raise TracError, 'Invalid email encoding setting: %s' % pref + + def notification_enabled(self, resid): + return self.config.getbool('notification', 'smtp_enabled') def notify(self, resid, subject): self.subject = subject - if not self.config.getbool('notification', 'smtp_enabled'): + if not self.notification_enabled(resid): return self.smtp_server = self.config['notification'].get('smtp_server') self.smtp_port = self.config['notification'].getint('smtp_port')
It should not be able to break any existing code anywhere as far as I can deduct, and would be a great bonus for reuse of Trac code.
Attachments
Change History
comment:1 Changed 5 years ago by nkantrowitz
- Milestone set to 0.11
comment:2 Changed 5 years ago by eblot
- Milestone changed from 0.11 to 0.11.1
While I agree the notification option should be improved, I don't think the proposal is the right way to do it.
A better option would be to enable notification on a per-component basis, without using a subclassed method. For example, for the ticket component, there should be a notification option:
[ticket] notification = enabled
The notify method would be called by the subclass if it is needed; no notification_enabled() should be required in the top-level class.
comment:3 Changed 5 years ago by osimons <simon-code@…>
I have a feeling we see this the same. We want to make it the responsibility of each component - or rather each component with some sort of email functionality that then would subclass NotifyEmail. That is what I want too, but my approach was made to ensure that nothing would break in existing code anywhere. Moving the setting up or down in the chain will have implications for third-party code, and would force these modules to implement code to handle enabled/disabled somehow.
This is not the time to break email behaviour - at least not because of a minor issue like this one. This ticket is only the small tip of the iceberg. There are heaps of pitfalls with subclassing NotifyEmail, and despite its documentation as a 'base class' it is really a part of ticket notification - making it approx. 700 lines covering 3 class definitions with mandatory call-chain for every major method… And, the [notification] section in trac.ini is really used and referred to as 'ticket notification' - not as 'project-wide general notification' settings.
I have since filing this ticket discovered more issues that for me breaks the whole idea of working within the bounds of the current NotifyEmail class.
I originally made my custom email class for 0.9 some 18 months ago, and back then I ended up essentially copying what I needed from NotifyEmail.notify() and NotifyEmail.send() methods, and bypassing them in the hierarchy call chain (they were somewhat smaller then). Re-doing everything for 0.11 I actually thought I could pull this off by 'tweaking' whenever I regained control of processing.
Now I see that in NotifyEmail.send() it always adds smtp_always_bcc and smtp_always_cc distribution lists, and this happens after I have lost control of processing. Adding distibution lists to password emails is not really what I want… The issue with notification enabled/disabled is minor in comparison, as for that I manage to reset it only a few non-dangerous statements later. Resetting the distribution lists can only happen after all emails have been been sent successfully - a virtual guarantee that those settings will be cleared in quite a short time… I suppose I now have to start adding code that loads my own disposable self.config that is not referenced by an Environment. It all starts adding up to way too many codelines to just send an email (110+ so far before starting on recent findings).
This leads me to the following conclusion: Either we..
a) extend the patch and add more supporting methods like add_additional_rcpts(self): ... return cc_rcpts, bcc_rcpts that provides more 'hooks' to rework the current behavior in subclasses. This behavior should not break subclasses.
or,
b) start talking about refactoring mail and notifications with improved core design, intended to be used by any module for various purposes, and that should also be able to handle alterantive transports (like SMS, IM, IRC or whatever). Perhaps also personalised preferences is high on that list for me - 'ticket notification is enabled but I do not want them', or even 'notify me of changesets by SMS'. Likely it should be based on the component architecture, and through Context provide a means to pass req, resource, and other attributes for reuse - no doubt 99,99% of emails happen in a request context where all this already exists.
or,
c) ignore it. Me and others copy whatever we need and implement our own things as whatever fits our needs - even if it means redoing the whole NotifyEmail.
Now, this turned out to be a longer comment than expected! And, perhaps trac-dev mailing list would be more appropriate - I'll start a thread there if my comments gain any interest.
comment:4 Changed 5 years ago by osimons <simon-code@…>
Oh, one more important thing: Permissions. Blindly adding people to all notifications without regarding what they are in fact allowed to see. Permission checking of smtp_always[b]cc is something that also belongs in the subclass or elsewhere close to the resource. Any permission or security policy could exclude a person from any given notification.
This flaw also exists in current TicketNotifyEmail that does no permissions checking for any receipient list. If at one time you commented on a ticket, you will forever get updates if something happens, regardless of your current status. This one is likely a separate issue, and intended only to note it here as it has bearing on the need for pathing/re-thinking notifications.
Ah. Just before hitting 'Submit', I found the thread from trac-dev mailing list discussion in february about notifications, and even see that there is an untouched notification branch created. Excuse my ignorance.
I'll gladly help out with this if I can be of assistance.
comment:5 Changed 5 years ago by osimons <simon-code@…>
- Milestone changed from 0.11.1 to 0.12
This won't make it for 0.11 - it needs quite a bit more work and clear thinking than I first imagined…
comment:6 Changed 14 months ago by thijstriemstra
- Cc thijstriemstra added
- Summary changed from [patch] Minor refactoring of general `NotifyEmail` class to make it easier to subclass to [patch] Refactoring of general `NotifyEmail` class to make it easier to subclass



+1 from me.