class CodeReviewNotifyEmail(NotifyEmail): template_name = "code_review_notify_email.cs" from_email = "codereview@localhost" ticket_pattern = re.compile('#([0-9]+)') def __init__(self, env): NotifyEmail.__init__(self, env) self.hdf = HDFWrapper(loadpaths=self.get_templates_dirs()) populate_hdf(self.hdf, env) def get_templates_dirs(self): from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def notify(self, cr_dict): self.cr_dict = cr_dict cr = CodeReview(self.env, cr_dict['cr_id']) cursor = self.env.get_db_cnx().cursor() cursor.execute("SELECT author, message FROM revision WHERE rev='%s'" % cr_dict['cr_id']) recordset = cursor.fetchall() if not recordset: return cs_author = recordset[0][0] cs_message = recordset[0][1] if cs_author == "anonymous" and cr_dict['cr_author'] == "anonymous": return subject = "[TracNotify] ChangeSet r%s by %s reviewed by %s" % \ (cr_dict['cr_id'], cs_author, ','.join(cr.get_reviewers())) if cr_dict['priority'] == 'critical': subject = '[Critical]' + subject ticket_info = self.get_ticket_info(cs_message, cr_dict['cr_message']) self.hdf['trac_name'] = self.env.config.get('project', 'name') absurl = self.env.config.get('codereview', 'absurl') self.hdf['absurl'] = absurl self.hdf['r_content'] = wiki_to_html(cr_dict['cr_message'], self.env, cr_dict['req'], absurls = absurl) self.hdf['ticket_len'] = len(ticket_info) self.hdf['t_info'] = ticket_info self.hdf['cs_id'] = cr_dict['cr_id'] self.hdf['cs_author'] = cs_author self.hdf['cs_message'] = wiki_to_html(cs_message, self.env, cr_dict['req'], absurls = absurl, escape_newlines=True) self.hdf['r_author'] = ', '.join(cr.get_reviewers()) self.hdf['r_priority'] = cr_dict['priority'] self.subject = subject self.smtp_server = self.config['notification'].get('smtp_server') self.smtp_port = self.config['notification'].getint('smtp_port') self.from_email = self.config['notification'].get('smtp_from') self.replyto_email = self.config['notification'].get('smtp_replyto') self.from_email = self.from_email or self.replyto_email if not self.from_email and not self.replyto_email: raise TracError(Markup('Unable to send email due to identity ' 'crisis.<p>Neither <b>notification.from</b> ' 'nor <b>notification.reply_to</b> are ' 'specified in the configuration.</p>'), 'SMTP Notification Error') # Authentication info (optional) self.user_name = self.config['notification'].get('smtp_user') self.password = self.config['notification'].get('smtp_password') Notify.notify(self, cr_dict['cr_id']) def get_reviewer_team(self): reviewer_team = self.env.config.get("codereview", "team_list", "") if reviewer_team: return [reviewer.strip() for reviewer in reviewer_team.split(',')] else: return [] def get_recipients(self, cr_id): cr = CodeReview(self.env, cr_id) reviewers = list(set(self.get_reviewer_team() + cr.get_reviewers())) cs_author = self.hdf.get('cs_author', None) if cs_author is None: cursor = self.env.get_db_cnx().cursor() cursor.execute("SELECT author FROM revision WHERE rev='%s'" % cr_id) cs_author = cursor.fetchone() if cs_author: cs_author = cs_author[0] else: cs_author = '' if cs_author in reviewers: reviewers.remove(cs_author) return ([cs_author,], reviewers) def get_ticket_info(self, cs_message, cr_message): cursor = self.env.get_db_cnx().cursor() ticket_ids = set(self.ticket_pattern.findall(cs_message or '') + self.ticket_pattern.findall(cr_message or '')) ticket_info = [] if(ticket_ids): cursor.execute('SELECT id, summary FROM ticket WHERE id in (%s) ORDER BY id' % ','.join(ticket_ids)) for id, summary in cursor.fetchall(): ticket_info.append({'id': id, 'summary': summary}) return ticket_info def send(self, torcpts, ccrcpts): from email.MIMEText import MIMEText from email.Utils import formatdate, formataddr body = self.hdf.render(self.template_name) projname = self.config.get('project', 'name') headers = {} headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__ headers['X-Trac-Version'] = __version__ headers['X-Trac-Project'] = projname headers['X-URL'] = self.config.get('project', 'url') headers['Subject'] = self.subject headers['From'] = (projname, self.from_email) headers['Sender'] = self.from_email headers['Reply-To'] = self.replyto_email def build_addresses(rcpts): """Format and remove invalid addresses""" return filter(lambda x: x, \ [self.get_smtp_address(addr) for addr in rcpts]) def remove_dup(rcpts, all): """Remove duplicates""" tmp = [] for rcpt in rcpts: if not rcpt in all: tmp.append(rcpt) all.append(rcpt) return (tmp, all) toaddrs = build_addresses(torcpts) ccaddrs = build_addresses(ccrcpts) recipients = [] (toaddrs, recipients) = remove_dup(toaddrs, recipients) (ccaddrs, recipients) = remove_dup(ccaddrs, recipients) if len(recipients) < 1: self.env.log.info('no recipient for a ticket notification') return headers['To'] = ', '.join(toaddrs) headers['Cc'] = ', '.join(ccaddrs) headers['Date'] = formatdate() if not self._charset.body_encoding: try: dummy = body.encode('ascii') except UnicodeDecodeError: raise TracError, "CodeReview contains non-Ascii chars. " \ "Please change encoding setting" msg = MIMEText(body) msg.set_type("text/html") del msg['Content-Transfer-Encoding'] msg.set_charset(self._charset) self.add_headers(msg, headers) self.env.log.debug("Sending SMTP notification to %s on port %d to %s" % (self.smtp_server, self.smtp_port, recipients)) msgtext = msg.as_string() # Ensure the message complies with RFC2822: use CRLF line endings recrlf = re.compile("\r?\n") msgtext = "\r\n".join(recrlf.split(msgtext)) self.server.sendmail(msg['From'], recipients, msgtext)
class CodeReviewNotifyEmail(NotifyEmail): template_name = "code_review_notify_email.cs" from_email = "codereview@localhost" ticket_pattern = re.compile('#([0-9]+)') def __init__(self, env): NotifyEmail.__init__(self, env) self.hdf = HDFWrapper(loadpaths=self.get_templates_dirs()) populate_hdf(self.hdf, env) def get_templates_dirs(self): from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def notify(self, cr_dict): self.cr_dict = cr_dict cr = CodeReview(self.env, cr_dict['cr_id']) cursor = self.env.get_db_cnx().cursor() cursor.execute("SELECT author, message FROM revision WHERE rev='%s'" % cr_dict['cr_id']) recordset = cursor.fetchall() if not recordset: return cs_author = recordset[0][0] cs_message = recordset[0][1] if cs_author == "anonymous" and cr_dict['cr_author'] == "anonymous": return subject = "[TracNotify] ChangeSet r%s by %s reviewed by %s" % \ (cr_dict['cr_id'], cs_author, ','.join(cr.get_reviewers())) if cr_dict['priority'] == 'critical': subject = '[Critical]' + subject ticket_info = self.get_ticket_info(cs_message, cr_dict['cr_message']) self.hdf['trac_name'] = self.env.config.get('project', 'name') absurl = self.env.config.get('codereview', 'absurl') self.hdf['absurl'] = absurl self.hdf['r_content'] = wiki_to_html(cr_dict['cr_message'], self.env, cr_dict['req'], absurls=absurl) self.hdf['ticket_len'] = len(ticket_info) self.hdf['t_info'] = ticket_info self.hdf['cs_id'] = cr_dict['cr_id'] self.hdf['cs_author'] = cs_author self.hdf['cs_message'] = wiki_to_html(cs_message, self.env, cr_dict['req'], absurls=absurl, escape_newlines=True) self.hdf['r_author'] = ', '.join(cr.get_reviewers()) self.hdf['r_priority'] = cr_dict['priority'] self.subject = subject self.smtp_server = self.config['notification'].get('smtp_server') self.smtp_port = self.config['notification'].getint('smtp_port') self.from_email = self.config['notification'].get('smtp_from') self.replyto_email = self.config['notification'].get('smtp_replyto') self.from_email = self.from_email or self.replyto_email if not self.from_email and not self.replyto_email: raise TracError( Markup('Unable to send email due to identity ' 'crisis.<p>Neither <b>notification.from</b> ' 'nor <b>notification.reply_to</b> are ' 'specified in the configuration.</p>'), 'SMTP Notification Error') # Authentication info (optional) self.user_name = self.config['notification'].get('smtp_user') self.password = self.config['notification'].get('smtp_password') Notify.notify(self, cr_dict['cr_id']) def get_reviewer_team(self): reviewer_team = self.env.config.get("codereview", "team_list", "") if reviewer_team: return [reviewer.strip() for reviewer in reviewer_team.split(',')] else: return [] def get_recipients(self, cr_id): cr = CodeReview(self.env, cr_id) reviewers = list(set(self.get_reviewer_team() + cr.get_reviewers())) cs_author = self.hdf.get('cs_author', None) if cs_author is None: cursor = self.env.get_db_cnx().cursor() cursor.execute("SELECT author FROM revision WHERE rev='%s'" % cr_id) cs_author = cursor.fetchone() if cs_author: cs_author = cs_author[0] else: cs_author = '' if cs_author in reviewers: reviewers.remove(cs_author) return ([ cs_author, ], reviewers) def get_ticket_info(self, cs_message, cr_message): cursor = self.env.get_db_cnx().cursor() ticket_ids = set( self.ticket_pattern.findall(cs_message or '') + self.ticket_pattern.findall(cr_message or '')) ticket_info = [] if (ticket_ids): cursor.execute( 'SELECT id, summary FROM ticket WHERE id in (%s) ORDER BY id' % ','.join(ticket_ids)) for id, summary in cursor.fetchall(): ticket_info.append({'id': id, 'summary': summary}) return ticket_info def send(self, torcpts, ccrcpts): from email.MIMEText import MIMEText from email.Utils import formatdate, formataddr body = self.hdf.render(self.template_name) projname = self.config.get('project', 'name') headers = {} headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__ headers['X-Trac-Version'] = __version__ headers['X-Trac-Project'] = projname headers['X-URL'] = self.config.get('project', 'url') headers['Subject'] = self.subject headers['From'] = (projname, self.from_email) headers['Sender'] = self.from_email headers['Reply-To'] = self.replyto_email def build_addresses(rcpts): """Format and remove invalid addresses""" return filter(lambda x: x, \ [self.get_smtp_address(addr) for addr in rcpts]) def remove_dup(rcpts, all): """Remove duplicates""" tmp = [] for rcpt in rcpts: if not rcpt in all: tmp.append(rcpt) all.append(rcpt) return (tmp, all) toaddrs = build_addresses(torcpts) ccaddrs = build_addresses(ccrcpts) recipients = [] (toaddrs, recipients) = remove_dup(toaddrs, recipients) (ccaddrs, recipients) = remove_dup(ccaddrs, recipients) if len(recipients) < 1: self.env.log.info('no recipient for a ticket notification') return headers['To'] = ', '.join(toaddrs) headers['Cc'] = ', '.join(ccaddrs) headers['Date'] = formatdate() if not self._charset.body_encoding: try: dummy = body.encode('ascii') except UnicodeDecodeError: raise TracError, "CodeReview contains non-Ascii chars. " \ "Please change encoding setting" msg = MIMEText(body) msg.set_type("text/html") del msg['Content-Transfer-Encoding'] msg.set_charset(self._charset) self.add_headers(msg, headers) self.env.log.debug("Sending SMTP notification to %s on port %d to %s" % (self.smtp_server, self.smtp_port, recipients)) msgtext = msg.as_string() # Ensure the message complies with RFC2822: use CRLF line endings recrlf = re.compile("\r?\n") msgtext = "\r\n".join(recrlf.split(msgtext)) self.server.sendmail(msg['From'], recipients, msgtext)