Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'mail-processor-send-to-project' of https://github.com/l…

…ettertwo/sentry into lettertwo-mail-processor-send-to-project

Conflicts:
	sentry/plugins/sentry_mail/__init__.py
	tests/sentry/plugins/mail/tests.py
  • Loading branch information...
commit 038f9c0462776190c6b71c779bc67a10c382cdcf 2 parents 3b30369 + 68c285a
David Cramer dcramer authored
3  sentry/plugins/base.py
View
@@ -180,6 +180,9 @@ def has_site_conf(self):
def has_project_conf(self):
return self.project_conf_form is not None
+ def get_form_initial(self, project=None):
+ return {}
+
# Response methods
def redirect(self, url):
69 sentry/plugins/sentry_mail/__init__.py
View
@@ -7,30 +7,46 @@
"""
from django import forms
from django.core.mail import EmailMultiAlternatives
+from django.core.validators import email_re, ValidationError
from django.template.loader import render_to_string
+from django.utils.translation import ugettext_lazy as _
from sentry.conf import settings
from sentry.plugins import Plugin, register
+import re
import pynliner
NOTSET = object()
+split_re = re.compile(r'\s*,\s*|\s+')
class MailConfigurationForm(forms.Form):
- send_to = forms.EmailField(max_length=128, widget=forms.TextInput(attrs={
- 'placeholder': 'you@example.com',
- }))
+ send_to = forms.CharField(label=_('Send to'), required=False,
+ help_text=_('Enter one or more emails separated by commas or lines. '
+ 'If no email addresses are entered, this value defaults to the Sentry Admins.'),
+ widget=forms.Textarea(attrs={
+ 'placeholder': 'you@example.com, \nother@example.com'}))
+ send_to_members = forms.BooleanField(label=_('Include project members'), initial=False, required=False,
+ help_text=_('Send emails to all members of this project.'))
+
+ def clean_send_to(self):
+ value = self.cleaned_data['send_to']
+ emails = filter(None, split_re.split(value))
+ for email in emails:
+ if not email_re.match(email):
+ raise ValidationError('%s is not a valid e-mail address.' % email)
+ return ','.join(emails)
@register
class MailProcessor(Plugin):
- title = 'Mail'
+ title = _('Mail')
conf_key = 'mail'
# site_conf_form = MailConfigurationForm
project_conf_form = MailConfigurationForm
def __init__(self, min_level=NOTSET, include_loggers=NOTSET, exclude_loggers=NOTSET,
- send_to=NOTSET, *args, **kwargs):
+ send_to=None, send_to_members=NOTSET, *args, **kwargs):
super(MailProcessor, self).__init__(*args, **kwargs)
@@ -40,24 +56,48 @@ def __init__(self, min_level=NOTSET, include_loggers=NOTSET, exclude_loggers=NOT
include_loggers = settings.MAIL_INCLUDE_LOGGERS
if exclude_loggers is NOTSET:
exclude_loggers = settings.MAIL_EXCLUDE_LOGGERS
- if send_to is NOTSET:
- send_to = ','.join(settings.ADMINS)
+ if send_to_members is NOTSET:
+ send_to_members = True
self.min_level = min_level
self.include_loggers = include_loggers
self.exclude_loggers = exclude_loggers
self.send_to = send_to
+ self.send_to_members = send_to_members
self.subject_prefix = settings.EMAIL_SUBJECT_PREFIX
def _send_mail(self, subject, body, html_body=None, project=None, fail_silently=True):
- send_to = self.get_option('send_to', project) or self.send_to
+ send_to = self.get_send_to(project)
subject_prefix = self.get_option('subject_prefix', project) or self.subject_prefix
- msg = EmailMultiAlternatives('%s%s' % (subject_prefix, subject), body, settings.SERVER_EMAIL, send_to.split(','))
+ msg = EmailMultiAlternatives('%s%s' % (subject_prefix, subject), body, settings.SERVER_EMAIL, send_to)
if html_body:
msg.attach_alternative(html_body, "text/html")
msg.send(fail_silently=fail_silently)
+ def get_send_to(self, project=None):
+ send_to_list = self.get_option('send_to', project)
+ if not send_to_list:
+ if self.send_to is not None:
+ send_to_list = self.send_to
+ elif project is not None:
+ send_to_list = project.member_set.values_list('user__email', flat=True)
+ else:
+ send_to_list = settings.ADMINS
+
+ if isinstance(send_to_list, basestring):
+ send_to_list = send_to_list.split(',')
+
+ send_to_list = set(send_to_list)
+
+ send_to_members = self.get_option('send_to_members', project)
+ if send_to_members is None:
+ send_to_members = self.send_to_members
+ if send_to_members:
+ send_to_list |= set(project.member_set.values_list('user__email', flat=True))
+
+ return filter(None, send_to_list)
+
def send_test_mail(self, project=None):
self._send_mail(
subject='Test Email',
@@ -66,7 +106,7 @@ def send_test_mail(self, project=None):
fail_silently=False,
)
- def mail_admins(self, group, event, fail_silently=False):
+ def mail_members(self, group, event, fail_silently=True):
project = group.project
interface_list = []
@@ -106,7 +146,7 @@ def mail_admins(self, group, event, fail_silently=False):
def should_mail(self, group, event):
project = group.project
- send_to = self.get_option('send_to', project) or self.send_to
+ send_to = self.get_send_to(project)
if not send_to:
return False
min_level = self.get_option('min_level', project) or self.min_level
@@ -120,6 +160,11 @@ def should_mail(self, group, event):
return False
return True
+ ## plugin hooks
+
+ def get_form_initial(self, project=None):
+ return {'send_to_members': True}
+
def post_process(self, group, event, is_new, is_sample, **kwargs):
if not is_new:
return
@@ -127,4 +172,4 @@ def post_process(self, group, event, is_new, is_sample, **kwargs):
if not self.should_mail(group, event):
return
- self.mail_admins(group, event)
+ self.mail_members(group, event)
4 sentry/templates/sentry/invalid_message_id.html
View
@@ -5,5 +5,7 @@
{% block title %}{% trans "Search" %} | {{ block.super }}{% endblock %}
{% block main %}
- <p>{% trans "We were unable to find a message matching the ID you entered." %}</p>
+ <section class="body">
+ <p>{% trans "We were unable to find a message matching the ID you entered." %}</p>
+ </section>
{% endblock %}
9 sentry/web/helpers.py
View
@@ -133,6 +133,7 @@ def plugin_config(plugin, project, request):
returns a tuple composed of a redirection boolean and the content to
be displayed.
"""
+ NOTSET = object()
plugin_key = plugin.get_conf_key()
if project:
@@ -142,14 +143,14 @@ def plugin_config(plugin, project, request):
form_class = plugin.site_conf_form
template = plugin.site_conf_template
- initials = {}
+ initials = plugin.get_form_initial(project)
for field in form_class.base_fields:
key = '%s:%s' % (plugin_key, field)
if project:
- value = ProjectOption.objects.get_value(project, key, None)
+ value = ProjectOption.objects.get_value(project, key, NOTSET)
else:
- value = Option.objects.get_value(key, None)
- if value:
+ value = Option.objects.get_value(key, NOTSET)
+ if value is not NOTSET:
initials[field] = value
form = form_class(
48 tests/sentry/plugins/mail/tests.py
View
@@ -50,7 +50,7 @@ def test_should_mail_match(self):
self.assertTrue(p.should_mail(group=group, event=Mock()))
@mock.patch('sentry.plugins.sentry_mail.MailProcessor._send_mail')
- def test_mail_admins_renders_interfaces(self, _send_mail):
+ def test_mail_members_renders_interfaces(self, _send_mail):
group = Mock(spec=Group)
group.first_seen = datetime.datetime.now()
group.get_absolute_url.return_value = '/example'
@@ -65,7 +65,51 @@ def test_mail_admins_renders_interfaces(self, _send_mail):
with self.Settings(SENTRY_URL_PREFIX='http://example.com'):
p = MailProcessor(send_to=['foo@example.com'])
- p.mail_admins(group, event)
+ p.mail_members(group, event)
stacktrace.get_title.assert_called_once_with()
stacktrace.to_string.assert_called_once_with(event)
+
+ def test_send_to(self):
+ Mock = mock.Mock
+ with mock.patch('sentry.models.ProjectOption.objects.get_value') as get_value:
+ opts = {}
+ get_value.side_effect = lambda p, k, d: opts.get(k, d)
+
+ admins = ['admin@fake.com']
+ member_emails = ['test@fake.com', 'member@fake.com']
+ project_emails = ['member@fake.com', 'new@fake.com']
+
+ project = Mock()
+ project.member_set = Mock()
+ project.member_set.values_list.return_value = member_emails
+
+ with mock.patch('sentry.plugins.sentry_mail.settings') as settings:
+ settings.ADMINS = admins
+
+ # member emails without admins
+ p = MailProcessor()
+ self.assertEqual(sorted(set(member_emails)),
+ sorted(p.get_send_to(project)))
+
+ # member emails with admins
+ p = MailProcessor()
+ opts = {'mail:send_to_admins': True}
+ p._send_mail('', '', project=project)
+ self.assertEqual(sorted(set(member_emails + admins)),
+ sorted(p.get_send_to(project)))
+
+ # project emails without admins
+ p = MailProcessor()
+ opts = {'mail:send_to': ','.join(project_emails)}
+ p._send_mail('', '', project=project)
+ self.assertEqual(sorted(set(project_emails)),
+ sorted(p.get_send_to(project)))
+
+ # project emails with admins
+ p = MailProcessor()
+ opts = {'mail:send_to': ','.join(project_emails),
+ 'mail:send_to_admins': True}
+ p._send_mail('', '', project=project)
+ self.assertEqual(sorted(set(project_emails + admins)),
+ sorted(p.get_send_to(project)))
Please sign in to comment.
Something went wrong with that request. Please try again.