Changeset 3041
- Timestamp:
- 01/12/08 00:19:17 (11 months ago)
- Files:
-
- announcerplugin/0.11/announcerplugin/api.py (modified) (3 diffs)
- announcerplugin/0.11/announcerplugin/distributors/email_distributor.py (moved) (moved from announcerplugin/0.11/announcerplugin/distributors/email.py) (5 diffs)
- announcerplugin/0.11/announcerplugin/distributors/__init__.py (modified) (1 diff)
- announcerplugin/0.11/announcerplugin/formatters/ticket_email.py (modified) (2 diffs)
- announcerplugin/0.11/announcerplugin/pref.py (modified) (3 diffs)
- announcerplugin/0.11/announcerplugin/resolvers/__init__.py (modified) (1 diff)
- announcerplugin/0.11/announcerplugin/resolvers/sessionemail.py (modified) (1 diff)
- announcerplugin/0.11/announcerplugin/resolvers/specified.py (modified) (1 diff)
- announcerplugin/0.11/announcerplugin/templates/ticket_email_mimic.html (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
announcerplugin/0.11/announcerplugin/api.py
r3040 r3041 3 3 from trac.db import Table, Column, Index 4 4 from trac.env import IEnvironmentSetupParticipant 5 import time 5 6 6 7 class IAnnouncementSubscriber(Interface): … … 104 105 """ 105 106 107 def format_subject(transport, realm, style, event): 108 """Returns a suitable subject line for the specified event.""" 109 106 110 class IAnnouncementDistributor(Interface): 107 111 """The Distributor is responsible for actually delivering an event to the … … 307 311 308 312 def send(self, evt): 313 start = time.time() 314 self._real_send(evt) 315 stop = time.time() 316 self.log.debug("AnnouncementSystem sent event in %s seconds." % (round(stop-start,2))) 317 318 def _real_send(self, evt): 309 319 """Accepts a single AnnouncementEvent instance (or subclass), and 310 320 returns nothing. announcerplugin/0.11/announcerplugin/distributors/email_distributor.py
r3040 r3041 10 10 from email.MIMEMultipart import MIMEMultipart 11 11 from email.MIMEText import MIMEText 12 12 import time, Queue, threading, smtplib 13 14 class DeliveryThread(threading.Thread): 15 def __init__(self, queue, sender): 16 threading.Thread.__init__(self) 17 self._sender = sender 18 self._queue = queue 19 self.setDaemon(True) 20 21 def run(self): 22 while 1: 23 sendfrom, recipients, message = self._queue.get() 24 25 self._sender(sendfrom, recipients, message) 26 13 27 class EmailDistributor(Component): 14 28 … … 93 107 If no prefix is desired, then specifying an empty option 94 108 will disable it.(''since 0.10.1'').""") 95 109 110 use_threaded_delivery = BoolOption('announcer', 'use_threaded_delivery', False, 111 """If true, the actual delivery of the message will occur in a separate thread.""") 112 113 def __init__(self): 114 if self.use_threaded_delivery: 115 self._deliveryQueue = Queue.Queue() 116 thread = DeliveryThread(self._deliveryQueue, self._transmit) 117 thread.start() 118 96 119 # IAnnouncementDistributor 97 120 def get_distribution_transport(self): … … 127 150 messages[format] = set() 128 151 129 # if name and not address: 130 # address = self._resolve 131 132 messages[format].add((name, address)) 152 if name and not address: 153 for resolver in self.resolvers: 154 address = resolver.get_address_for_name(name) 155 if address: 156 break 157 158 if address: 159 messages[format].add((name, address)) 160 else: 161 self.log.debug("EmailDistributor was unable to find an address for: %s" % name) 133 162 134 163 for format in messages.keys(): 135 # print 'ER', messages[format] 136 # self.log.debug( 137 # "EmailDistributor is sending event as '%s' to: %s" % ( 138 # format, ', '.join(messages[format]) 139 # ) 140 # ) 141 self._do_send(transport, event, format, messages[format], formats[format]) 164 if messages[format]: 165 self.log.debug( 166 "EmailDistributor is sending event as '%s' to: %s" % ( 167 format, ', '.join( 168 ('%s <%s>' % (name, address) for name, address in messages[format]) 169 ) 170 ) 171 ) 172 self._do_send(transport, event, format, messages[format], formats[format]) 142 173 143 174 def _get_default_format(self): … … 166 197 def _do_send(self, transport, event, format, recipients, formatter, backup=None): 167 198 output = formatter.format(transport, event.realm, format, event) 199 subject = formatter.format_subject(transport, event.realm, format, event) 168 200 169 201 parentMessage = MIMEMultipart("related") 170 parentMessage['Subject'] = "Message Subject" 171 parentMessage['From'] = 'Bob' 172 parentMessage['To'] = 'Sam' 202 parentMessage['Subject'] = subject 203 parentMessage['From'] = self.smtp_from 204 parentMessage['To'] = self.env.project_name 205 parentMessage['Reply-To'] = self.smtp_replyto 173 206 parentMessage.preamble = 'This is a multi-part message in MIME format.' 174 207 … … 176 209 parentMessage.attach(msgText) 177 210 178 import smtplib 211 start = time.time() 212 213 package = (self.smtp_from, [x[1] for x in recipients if x], parentMessage.as_string() ) 214 if self.use_threaded_delivery: 215 self._deliveryQueue.put(package) 216 else: 217 self._transmit(*package) 218 219 stop = time.time() 220 self.log.debug("EmailDistributor took %s seconds to send." % (round(stop-start,2))) 221 222 def _transmit(self, smtpfrom, addresses, message): 179 223 smtp = smtplib.SMTP() 180 224 smtp.connect(self.smtp_server) 181 225 smtp.login(self.smtp_user, self.smtp_password) 182 for name, address in recipients: 183 smtp.sendmail(self.smtp_from, address, parentMessage.as_string) 226 smtp.sendmail(smtpfrom, addresses, message) 184 227 smtp.quit() 185 228 186 229 # IAnnouncementDistributor 187 230 def get_announcement_preference_boxes(self, req): announcerplugin/0.11/announcerplugin/distributors/__init__.py
r3015 r3041 1 import email 1 import email_distributor announcerplugin/0.11/announcerplugin/formatters/ticket_email.py
r3040 r3041 33 33 default_email_format = Option('announcer', 'default_email_format', 'plaintext') 34 34 35 ticket_email_subject = Option('announcer', 'ticket_email_subject', "Ticket #${ticket.id}: ${ticket['summary']}") 36 35 37 def get_format_transport(self): 36 38 return "email" … … 48 50 49 51 return 50 52 53 def format_subject(self, transport, realm, style, event): 54 if transport == "email": 55 if realm == "ticket": 56 template = NewTextTemplate(self.ticket_email_subject) 57 return template.generate(ticket=event.target, event=event).render() 58 51 59 def format(self, transport, realm, style, event): 52 if realm == "ticket": 53 if hasattr(self, '_format_%s' % style): 54 return getattr(self, '_format_%s' % style)(event) 55 56 def _load_text_template(self, chrome, filename): 57 # print 'Load', chrome.templates 58 if not chrome.templates: 59 return None 60 61 return chrome.templates.load(filename, cls=NewTextTemplate) 60 if transport == "email": 61 if realm == "ticket": 62 if hasattr(self, '_format_%s' % style): 63 return getattr(self, '_format_%s' % style)(event) 62 64 63 65 def _format_plaintext(self, event): announcerplugin/0.11/announcerplugin/pref.py
r3016 r3041 8 8 9 9 def truth(v): 10 print 'V=', v11 10 if v in (False, 'False', 'false', 0, '0', ''): 12 print 'false'13 11 return None 14 print 'true'15 12 return True 16 13 17 14 class AnnouncerPreferences(Component): 18 implements(IPreferencePanelProvider, ITemplateProvider , IRequestHandler)15 implements(IPreferencePanelProvider, ITemplateProvider) 19 16 20 17 preference_boxes = ExtensionPoint(IAnnouncementPreferenceProvider) … … 26 23 resource_dir = resource_filename(__name__, 'templates') 27 24 return [resource_dir] 28 25 29 26 def get_preference_panels(self, req): 30 print 'REQ', req.authname31 27 if req.authname and req.authname != 'anonymous': 32 28 yield ('announcer', 'Announcements') … … 62 58 return 'prefs_announcer.html', {"boxes": streams, "style": style.render()} 63 59 64 def match_request(self, req):65 print "MATCH?!"66 print req67 print req.path_info68 return False69 60 70 def process_request(self, req):71 return '', {}72 announcerplugin/0.11/announcerplugin/resolvers/__init__.py
r3015 r3041 1 1 import specified 2 2 import sessionemail 3 import defaultdomain announcerplugin/0.11/announcerplugin/resolvers/sessionemail.py
r3015 r3041 16 16 WHERE sid=%s 17 17 AND name=%s 18 """, ( sid, 'email'))18 """, (name, 'email')) 19 19 20 20 for record in sorted(cursor.fetchall(), key=lambda x: x[1]): announcerplugin/0.11/announcerplugin/resolvers/specified.py
r3040 r3041 19 19 AND authenticated=1 20 20 AND name=%s 21 """, ( sid,'specified_email'))21 """, (name,'specified_email')) 22 22 23 23 result = cursor.fetchone() announcerplugin/0.11/announcerplugin/templates/ticket_email_mimic.html
r3040 r3041 113 113 114 114 </head> 115 <body bgcolor="white" >116 <span class="header" >Ticket #${ticket.id} (${ticket['status']} ${ticket['type']})</span>117 <div class="ticketbox" >118 <div class="title" >${ticket['summary']}</div>119 <table class="header" >115 <body bgcolor="white" style='font: medium "Lucida Grande", Lucida, Verdana, sans-serif'> 116 <span class="header" style="font-size: large;">Ticket #${ticket.id} (${ticket['status']} ${ticket['type']})</span> 117 <div class="ticketbox" style="background-color: #fefcd3; width: 90%; padding: .5em 1em; border-style: outset; border-width: 1px; margin: 2px;"> 118 <div class="title" style="font-weight: bold; border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em;">${ticket['summary']}</div> 119 <table class="header" width="100%"> 120 120 <tr> 121 <td class="label" >Owned by:</td>122 <td class="value" >${ticket['owner']}</td>121 <td class="label" width="30%" style="font-size: x-small; color: #7e7e7e; border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; width: 30%;">Owned by:</td> 122 <td class="value" width="70%" style="border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; font-size: small;">${ticket['owner']}</td> 123 123 </tr> 124 124 <tr> 125 <td class="label" >Reported by:</td>126 <td class="value" >${ticket['reporter']}</td>125 <td class="label" style="font-size: x-small; color: #7e7e7e; border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; width: 30%;">Reported by:</td> 126 <td class="value" style="border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; font-size: small;">${ticket['reporter']}</td> 127 127 </tr> 128 128 <tr py:if="ticket['milestone']"> 129 <td class="label" >Milestone:</td>130 <td class="value" >${ticket['milestone']}</td>129 <td class="label" style="font-size: x-small; color: #7e7e7e; border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; width: 30%;">Milestone:</td> 130 <td class="value" style="border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; font-size: small;">${ticket['milestone']}</td> 131 131 </tr> 132 132 <tr py:if="ticket['priority']"> 133 <td class="label" >Priority:</td>134 <td class="value" >${ticket['priority']}</td>133 <td class="label" style="font-size: x-small; color: #7e7e7e; border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; width: 30%;">Priority:</td> 134 <td class="value" style="border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; font-size: small;">${ticket['priority']}</td> 135 135 </tr> 136 136 <tr py:if="ticket['severity']"> 137 <td class="label" >Severity:</td>138 <td class="value" >${ticket['severity']}</td>137 <td class="label" style="font-size: x-small; color: #7e7e7e; border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; width: 30%;">Severity:</td> 138 <td class="value" style="border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; font-size: small;">${ticket['severity']}</td> 139 139 </tr> 140 140 </table> 141 141 <py:if test="category == 'created'"> 142 <div class="descheader" >Description</div>143 <div class="descbody" >${ticket['description']}</div>142 <div class="descheader" style="font-size: x-small; color: #7e7e7e; border-bottom: 1pt inset #dfdfdf; padding-bottom: .5em; font-weight: bold;">Description</div> 143 <div class="descbody" style="font-size: small; padding: 1em;">${ticket['description']}</div> 144 144 </py:if> 145 145 </div> 146 146 <py:if test="has_changes"> 147 <div class="changetitle" >Changes (by ${author}):</div>147 <div class="changetitle" style="font-size: small; margin: 1em;">Changes (by ${author}):</div> 148 148 <ul> 149 <li py:for="change in short_changes" class="changeitem" >150 <span class="fieldname" >${change}</span> changed151 from <span class="from" >${short_changes[change][0]}</span>152 to <span class="to" >${short_changes[change][1]}</span>.149 <li py:for="change in short_changes" class="changeitem" style="font-size: small;"> 150 <span class="fieldname" style="font-weight: bold; font-style: italic;">${change}</span> changed 151 from <span class="from" style="font-weight: bold;">${short_changes[change][0]}</span> 152 to <span class="to" style="font-weight: bold;">${short_changes[change][1]}</span>. 153 153 </li> 154 154 </ul> 155 155 <ul> 156 <li py:for="(change, content) in long_changes.items()" class="longchange" >157 <span class="fieldname" >${change}:</span>158 <div class="htmldiff" >${content}</div>156 <li py:for="(change, content) in long_changes.items()" class="longchange" style="font-size: small;"> 157 <span class="fieldname" style="font-weight: bold; font-style: italic;">${change}:</span> 158 <div class="htmldiff" style="margin-left: 2em; padding: 1em; width: 90%; background-color: #f3f3f3; margin: 5px; border-style: outset; border-width: 1px;">${content}</div> 159 159 </li> 160 160 </ul> 161 161 </py:if> 162 162 <py:if test="comment"> 163 <div class="commentstitle" >Comments:</div>164 <div class="comments" >${comment}</div>163 <div class="commentstitle" style="font-size: small; margin: 1em;">Comments:</div> 164 <div class="comments" style="font-size: small; margin: 0 0 0 2em; font-style: italic;">${comment}</div> 165 165 </py:if> 166 166 <br /> 167 167 <hr /> 168 168 <br /> 169 <div class="footer" >Ticket URL: <a href="${ticket_link}">${ticket_link}</a><br />169 <div class="footer" style="font-size: small;">Ticket URL: <a href="${ticket_link}">${ticket_link}</a><br /> 170 170 ${project_name} <a href="${project_link}">${project_link}</a><br /> 171 171 ${project_desc}<br />
