Ticket #222: patch-emailnotify-r1120.diff
| File patch-emailnotify-r1120.diff, 13.9 kB (added by cmlenz, 4 years ago) |
|---|
-
wiki-default/TracIni
32 32 || default_priority || Default priority for newly created tickets || 33 33 || default_milestone || Default milestone for newly created tickets || 34 34 || default_component || Default component for newly created tickets || 35 || restrict_owner || Select ticket owner from combo box that lists all logged users || 35 36 36 37 See also: TracTicketsCustomFields 37 38 … … 39 40 || max_size || Maximum allowed file size for ticket and wiki attachments || 40 41 41 42 == [notification] == 42 || smtp_enabled || Enable SMTP (email) notification (true, false) || 43 || smtp_server || SMTP server to use for email notifications || 44 || smtp_from || Sender address to use in notification emails || 45 || smtp_replyto || Reply-To address to use in notification emails || 46 || smtp_always_cc || Email address(es) to always send notifications to || 43 || smtp_enabled || Enable SMTP (email) notification (true, false) || 44 || smtp_server || SMTP server to use for email notifications || 45 || smtp_from || Sender address to use in notification emails || 46 || smtp_replyto || Reply-To address to use in notification emails || 47 || smtp_always_cc || Email address(es) to always send notifications to || 48 || always_notify_owner || Always send notifications to ticket owners || 47 49 || always_notify_reporter || Always send notifications to any address in the ''reporter'' field || 48 50 49 51 See also: TracNotification -
wiki-default/TracNotification
21 21 * '''smtp_from''': Email address to use for ''Sender''-headers in notification emails. 22 22 * '''smtp_replyto''': Email address to use for ''Reply-To''-headers in notification emails. 23 23 * '''smtp_always_cc''': List of email addresses to always send notifications to. ''Typically used to post ticket changes to a dedicated mailing list.'' 24 * '''always_notify_owner''': Always send notifications to ticket owners. 24 25 * '''always_notify_reporter''': Always send notifications to any address in the reporter field. 25 26 26 27 Either '''smtp_from''' or '''smtp_replyto''' (or both) ''must'' be set, otherwise Trac refuses to send notification mails. -
trac/db_default.py
431 431 ('ticket', 'default_priority', 'normal'), 432 432 ('ticket', 'default_milestone', ''), 433 433 ('ticket', 'default_component', 'component1'), 434 ('ticket', 'restrict_owner', 'false'), 434 435 ('header_logo', 'link', 'http://trac.edgewall.com/'), 435 436 ('header_logo', 'src', 'trac_banner.png'), 436 437 ('header_logo', 'alt', 'Trac'), … … 442 443 ('notification', 'smtp_enabled', 'false'), 443 444 ('notification', 'smtp_server', 'localhost'), 444 445 ('notification', 'smtp_always_cc', ''), 446 ('notification', 'always_notify_owner', 'false'), 445 447 ('notification', 'always_notify_reporter', 'false'), 446 448 ('notification', 'smtp_from', 'trac@localhost'), 447 449 ('notification', 'smtp_replyto', 'trac@localhost'), -
trac/Session.py
104 104 self.sid = sid 105 105 curs = self.db.cursor() 106 106 curs.execute("SELECT username,var_name,var_value FROM session" 107 " WHERE sid=%s", self.sid)107 " WHERE sid=%s", self.sid) 108 108 rows = curs.fetchall() 109 109 if (not rows # No session data yet 110 110 or rows[0][0] == 'anonymous' # Anon session 111 111 or rows[0][0] == self.req.authname): # Session is mine 112 112 for u,k,v in rows: 113 113 self.vars[k] = v 114 # For authenticated users: Load global settings for new session 115 if self.req.authname != 'anonymous': 116 curs.execute("SELECT var_name,var_value FROM session" 117 " WHERE sid ISNULL AND username = %s", 118 self.req.authname) 119 rows = curs.fetchall() 120 for k,v in rows: 121 if not self.vars.has_key(k): self.vars[k] = v 114 122 self.update_sess_time() 115 123 self.bake_cookie() 116 124 self.populate_hdf() … … 143 151 curs.execute('UPDATE session SET username=%s,var_value=%s' 144 152 ' WHERE sid=%s AND var_name=%s', 145 153 self.req.authname, val, self.sid, key) 154 # For authenticated users: Set global settings (ignore session-specific 155 # attributes) 156 if self.req.authname != 'anonymous' and \ 157 not key in ['mod_time', 'last_visit']: 158 curs.execute("DELETE FROM session WHERE sid ISNULL AND" 159 " username = %s AND var_name = %s", 160 self.req.authname, key) 161 curs.execute("INSERT INTO session(sid,username,var_name,var_value)" 162 " VALUES(NULL,%s,%s,%s)", self.req.authname, key, val) 146 163 self.db.commit() 147 164 self.vars[key] = val 148 165 149 166 def create_new_sid(self): 150 167 self.sid = hex_entropy(24) 168 # For authenticated users: Load (existing) global settings for new session 169 if self.req.authname != 'anonymous': 170 curs = self.db.cursor() 171 curs.execute("SELECT var_name,var_value FROM session" 172 " WHERE sid ISNULL AND username = %s", self.req.authname) 173 rows = curs.fetchall() 174 for k,v in rows: 175 if not self.vars.has_key(k): self.vars[k] = v 151 176 self.bake_cookie() 152 177 self.populate_hdf() 153 178 … … 173 198 curs.execute("DELETE FROM session WHERE sid IN" 174 199 " (SELECT sid FROM session WHERE var_name='mod_time'" 175 200 " AND var_value < %i)", mintime) 201 # For authenticated users: Delete global settings when all sessions are removed 202 curs.execute("DELETE FROM session WHERE sid ISNULL AND username NOT IN" 203 " (SELECT username FROM session WHERE NOT sid ISNULL)") 176 204 self.db.commit() 177 -
trac/Notify.py
24 24 import time 25 25 import smtplib 26 26 import os.path 27 import email.Utils 27 28 28 29 import neo_cgi 29 30 import neo_cs … … 106 107 from_email = 'trac+tickets@localhost' 107 108 subject = '' 108 109 server = None 110 useremails = {} 109 111 112 def __init__(self, env, msg_template): 113 Notify.__init__(self, env, msg_template) 114 cursor = self.db.cursor() 115 cursor.execute("SELECT username,var_value FROM session WHERE sid ISNULL AND var_name = 'email'") 116 rows = cursor.fetchall() 117 for k,v in rows: self.useremails[k] = v 118 110 119 def notify(self, resid, subject): 111 120 self.subject = subject 112 121 … … 131 140 Notify.notify(self, resid) 132 141 133 142 def get_email_addresses(self, txt): 134 import email.Utils135 emails = [x[1] for x in email.Utils.getaddresses([str(txt)])]143 emails = [x[1] for x in email.Utils.getaddresses([str(txt), \ 144 self.useremails.has_key(txt) and self.useremails[txt] or ''])] 136 145 return filter(lambda x: x.find('@') > -1, emails) 137 146 138 147 def begin_send(self): … … 279 288 return txt 280 289 281 290 def parse_cc(self, txt): 282 return filter(lambda x: '@' in x, txt.replace(',', ' ').split()) 291 recipients = txt.replace(',', ' ').split() 292 additional = [] 293 for r in recipients: 294 if self.useremails.has_key(r): 295 additional.append(self.useremails[r]) 296 emails = [x[1] for x in email.Utils.getaddresses(recipients + additional)] 297 return filter(lambda x: '@' in x, emails) 283 298 284 299 def format_hdr(self): 285 300 return '#%s: %s' % (self.ticket['id'], … … 295 310 # is set to true 296 311 val = self.env.get_config('notification', 'always_notify_reporter', 'false') 297 312 notify_reporter = val.lower() in TRUE 313 val = self.env.get_config('notification', 'always_notify_owner', 'false') 314 notify_owner = val.lower() in TRUE 298 315 299 316 emails = self.prev_cc 300 317 cursor = self.db.cursor() 301 318 # Harvest email addresses from the cc field 302 cursor.execute('SELECT cc,reporter FROM ticket WHERE id=%s', tktid)319 cursor.execute('SELECT cc,reporter,owner FROM ticket WHERE id=%s', tktid) 303 320 row = cursor.fetchone() 304 321 if row: 305 322 emails += row[0] and self.parse_cc(row[0]) or [] 306 323 if notify_reporter: 307 324 emails += row[1] and self.get_email_addresses(row[1]) or [] 325 if notify_owner: 326 emails += row[2] and self.get_email_addresses(row[2]) or [] 308 327 309 328 if notify_reporter: 310 329 cursor.execute('SELECT DISTINCT author,ticket FROM ticket_change ' -
trac/Ticket.py
196 196 return log 197 197 198 198 199 def get_users(env, db): 200 users = [] 201 option = env.get_config('ticket', 'restrict_owner') 202 if option.lower() in util.TRUE: 203 cursor = db.cursor() 204 cursor.execute("SELECT DISTINCT s1.username, s2.var_value " 205 "FROM session s1 " 206 "LEFT OUTER JOIN session s2 " 207 " ON (s2.sid ISNULL AND s1.username = s2.username " 208 " AND s2.var_name = 'name') " 209 "WHERE s1.sid ISNULL " 210 "ORDER BY s1.username") 211 while 1: 212 row = cursor.fetchone() 213 if not row: 214 break 215 user = {'name': row[0]} 216 if row[1]: 217 user['label'] = '%s (%s)' % (row[0], row[1]) 218 users.append(user) 219 return users 220 221 199 222 def cmp_by_order(a, b): 200 223 try: 201 224 return int(a['order']) - int(b['order']) … … 334 357 " ORDER BY value", 335 358 self.req.hdf, 'enums.severity') 336 359 360 util.add_to_hdf(get_users(self.env, self.db), self.req.hdf, 361 'newticket.users') 362 337 363 insert_custom_fields(self.env, self.req.hdf, ticket) 338 364 339 365 … … 402 428 util.sql_to_hdf(self.db, "SELECT name FROM enum WHERE type='resolution'" 403 429 " ORDER BY value", 404 430 self.req.hdf, 'enums.resolution') 431 432 util.add_to_hdf(get_users(self.env, self.db), self.req.hdf, 433 'ticket.users') 434 435 405 436 util.hdf_add_if_missing(self.req.hdf, 'ticket.components', ticket['component']) 406 437 util.hdf_add_if_missing(self.req.hdf, 'ticket.milestones', ticket['milestone']) 407 438 util.hdf_add_if_missing(self.req.hdf, 'ticket.versions', ticket['version']) -
templates/ticket.cs
229 229 <?cs call:hdf_select(enums.resolution, "resolve_resolution", args.resolve_resolution) ?><br /> 230 230 <?cs call:action_radio('reassign') ?> 231 231 <label for="reassign">reassign</label> 232 <label for="reassign_owner">to:</label> 233 <input type="text" id="reassign_owner" name="reassign_owner" size="40" value="<?cs 234 if:args.reassign_to ?><?cs var:args.reassign_to ?><?cs 235 else ?><?cs var:trac.authname ?><?cs /if ?>" /><?cs 232 <label for="reassign_owner">to:</label><?cs 233 if:args.reassign_to ?><?cs 234 set default_owner=args.reassign_to ?><?cs 235 else ?><?cs 236 set default_owner=trac.authname ?><?cs 237 /if ?><?cs 238 if:len(ticket.users) ?><?cs 239 call:hdf_select(ticket.users, "reassign_owner", default_owner) ?><?cs 240 else ?> 241 <input type="text" id="reassign_owner" name="reassign_owner" size="40" 242 value="<?cs var:default_owner ?>" /><?cs 243 /if ?><?cs 236 244 /if ?><?cs 237 245 if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 238 246 <script type="text/javascript"> -
templates/macros.cs
1 1 <?cs def:hdf_select(options, name, selected) ?> 2 2 <select size="1" id="<?cs var:name ?>" name="<?cs var:name ?>"><?cs 3 each:option = options ?><?cs 4 if option.name == $selected ?> 5 <option selected="selected"><?cs var:option.name ?></option><?cs 6 else ?> 7 <option><?cs var:option.name ?></option><?cs 8 /if ?><?cs 3 each:option = options ?> 4 <option<?cs 5 if:option.name == selected ?> selected="selected"<?cs /if ?><?cs 6 if:option.label ?> value="<?cs var:option.name ?>"<?cs /if ?>><?cs 7 alt:option.label ?><?cs var:option.name ?><?cs /alt ?></option><?cs 9 8 /each ?> 10 9 </select><?cs 11 10 /def?> -
templates/newticket.cs
59 59 <label for="milestone">Milestone:</label><?cs 60 60 call:hdf_select(newticket.milestones, "milestone", newticket.milestone) ?><br /> 61 61 <label for="owner">Assign to:</label> 62 <?cs if:len(newticket.users) ?><?cs 63 call:hdf_select(newticket.users, "owner", newticket.owner) ?><br /><?cs 64 else ?> 62 65 <input type="text" id="owner" name="owner" size="20" value="<?cs 63 66 var:newticket.owner ?>" /><br /> 67 <?cs /if ?> 64 68 <label for="cc">Cc:</label> 65 69 <input type="text" id="cc" name="cc" size="30" value="<?cs var:newticket.cc ?>" /> 66 70 </div>
