Skip to content

Commit

Permalink
Initial work on new mail settings
Browse files Browse the repository at this point in the history
  • Loading branch information
dcramer committed Jun 27, 2013
1 parent 64a791e commit c533ca3
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 62 deletions.
39 changes: 1 addition & 38 deletions src/sentry/plugins/bases/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
from sentry.plugins import Plugin
from sentry.models import UserOption, Project, User, AccessGroup
from sentry.models import UserOption, User, AccessGroup
from sentry.utils.cache import cache
from sentry.constants import MEMBER_USER


class NotificationConfigurationForm(forms.Form):
Expand All @@ -33,39 +32,6 @@ def save(self):
raise NotImplementedError


class NotificationUserOptionsForm(BaseNotificationUserOptionsForm):
projects = forms.MultipleChoiceField(choices=(), widget=forms.CheckboxSelectMultiple(), required=False)

def __init__(self, *args, **kwargs):
super(NotificationUserOptionsForm, self).__init__(*args, **kwargs)
user = self.user
self.project_list = dict(
(p.slug, p) for p in
Project.objects.get_for_user(user, access=MEMBER_USER)
)
project_choices = sorted((p.slug, p.name) for p in self.project_list.values())
self.fields['projects'].choices = project_choices
self.fields['projects'].widget.choices = self.fields['projects'].choices

enabled_projects = []
for slug, project in self.project_list.iteritems():
is_enabled = self.plugin.get_option('alert', project=project, user=user)
if is_enabled == 1 or is_enabled is None:
enabled_projects.append(slug)
self.fields['projects'].initial = enabled_projects

def get_description(self):
return _('Send notifications when a new event is seen, or when an '
'already resolved event has changed back to unresolved.')

def save(self):
user = self.user
projects = self.cleaned_data.get('projects')

for slug, project in self.project_list.iteritems():
self.plugin.set_option('alert', int(slug in projects), user=user, project=project)


class Message(object):
def __init__(self, short, long):
self.short = short
Expand Down Expand Up @@ -190,9 +156,6 @@ def post_process(self, group, event, is_new, is_sample, **kwargs):

self.notify_users(group, event)

def get_notification_forms(self, **kwargs):
return [NotificationUserOptionsForm]

# Backwards-compatibility
NotifyConfigurationForm = NotificationConfigurationForm
NotifyPlugin = NotificationPlugin
68 changes: 63 additions & 5 deletions src/sentry/templates/sentry/account/notifications.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,81 @@
{% block title %}{% trans "Notification Settings" %} | {{ block.super }}{% endblock %}

{% block inner %}
<style type="text/css">
table label,
table input[type=checkbox] {
margin: 0;
}
table caption {
font-weight: normal;
text-align: left;
font-size: 16px;
padding-bottom: 10px;
}
</style>

<form action="" method="post">
{% csrf_token %}
{% for form in forms %}
{{ form|as_crispy_errors }}

<p>{% blocktrans %}Notifications are generally sent when a new event is
seen, an already resolved event has changed to unresolved, or a
system-wide spike in events is detected.{% endblocktrans %}</p>

{% with settings_form as form %}
<fieldset>
<div><legend>{% trans "General" %}</legend></div>
{% include "sentry/partial/form_base.html" %}
</fieldset>
{% endwith %}

{% if project_forms %}
<fieldset>
<div><legend>{% trans "Mail" %}</legend></div>
<p>{% blocktrans %}Choose which projects you wish to receive email notifications for, as well as an alternative email address to send to.{% endblocktrans %}</p>

{% for project, form in project_forms %}
{{ form|as_crispy_errors }}
{% endfor %}
{% for project, form in project_forms %}
{% ifchanged project.team %}
{% if not forloop.first %}
</table>
{% endif %}
<table class="table table-bordered table-striped">
<caption>{{ project.team.name }}</caption>

<thead>
<tr>
<th style="width:5px"><input type="checkbox" onclick="$('tbody input[type=checkbox]', $(this).parents('table')).attr('checked', this.checked)"></th>
<th>{% trans "Project Name" %}</th>
<th style="width:100px;overflow:hidden;text-align:right">
{% trans "Email Address" %}</th>
</tr>
</thead>
<tbody>
{% endifchanged %}
<tr>
<td>{{ form.alert }}</td>
<td><label for="{{ form.alert.auto_id }}">{{ project.name }}</label></td>
<td style="text-align:right"><a href="#"><em>default</em></a></td>
</tr>
{% endfor %}
</tbody></table>
</fieldset>
{% endif %}

{% for form in ext_forms %}
<fieldset>
<div><legend>{{ form.get_title }}</legend></div>
{% with form.get_description as description %}
{% if description %}
{{ description|linebreaks }}
{% endif %}
{% endwith %}
{% for field in form %}
{{ field|as_crispy_field }}
{% endfor %}
{% include "sentry/partial/form_base.html" %}
</fieldset>
{% endfor %}

<fieldset class="form-actions">
<button type="submit" class="btn btn-primary">{% trans "Save Changes" %}</button>
</fieldset>
Expand Down
8 changes: 2 additions & 6 deletions src/sentry/templates/sentry/partial/_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@

<form action="" method="post">
{% csrf_token %}
{{ form|as_crispy_errors }}

{% for field in form %}
{{ field|as_crispy_field }}
{% endfor %}
{% include "sentry/partial/form_base.html" %}

<fieldset class="form-actions">
<button type="submit" class="btn btn-primary">{% if submit_label %}{{ submit_label }}{% else %}{% trans "Save Changes" %}{% endif %}</button>
</fieldset>
</form>
</form>
7 changes: 7 additions & 0 deletions src/sentry/templates/sentry/partial/form_base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% load crispy_forms_tags %}

{{ form|as_crispy_errors }}

{% for field in form %}
{{ field|as_crispy_field }}
{% endfor %}
26 changes: 26 additions & 0 deletions src/sentry/web/forms/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,29 @@ def clean_user(self):

class ChangePasswordRecoverForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput())


class ProjectEmailOptionsForm(forms.Form):
alert = forms.BooleanField(required=False)
email = forms.EmailField(required=False)

def __init__(self, project, user, *args, **kwargs):
self.project = project
self.user = user

super(ProjectEmailOptionsForm, self).__init__(*args, **kwargs)

is_enabled = UserOption.objects.get_value(
user, project, 'mail:alert', None)
if is_enabled is None:
is_enabled = True
else:
is_enabled = bool(is_enabled)

self.fields['alert'].initial = is_enabled

def save(self):
UserOption.objects.set_value(
self.user, self.project, 'mail:alert',
int(self.cleaned_data['alert']),
)
45 changes: 32 additions & 13 deletions src/sentry/web/frontend/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
:copyright: (c) 2012 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
import itertools

from collections import defaultdict

from django.conf import settings as dj_settings
from django.contrib import messages
from django.contrib.auth import login as login_user, authenticate
Expand All @@ -16,11 +20,14 @@
from django.views.decorators.csrf import csrf_protect
from django.utils import timezone

from sentry.models import UserOption, LostPasswordHash
from sentry.constants import MEMBER_USER
from sentry.models import Project, UserOption, LostPasswordHash
from sentry.plugins import plugins
from sentry.web.decorators import login_required
from sentry.web.forms.accounts import AccountSettingsForm, NotificationSettingsForm, \
AppearanceSettingsForm, RegistrationForm, RecoverPasswordForm, ChangePasswordRecoverForm
from sentry.web.forms.accounts import (
AccountSettingsForm, NotificationSettingsForm, AppearanceSettingsForm,
RegistrationForm, RecoverPasswordForm, ChangePasswordRecoverForm,
ProjectEmailOptionsForm)
from sentry.web.helpers import render_to_response
from sentry.utils.auth import get_auth_providers
from sentry.utils.safe import safe_execute
Expand Down Expand Up @@ -219,29 +226,41 @@ def appearance_settings(request):
@login_required
@transaction.commit_on_success
def notification_settings(request):
forms = []
settings_form = NotificationSettingsForm(request.user, request.POST or None)

project_list = Project.objects.get_for_user(request.user, access=MEMBER_USER)
project_forms = [
(project, ProjectEmailOptionsForm(
project, request.user,
request.POST or None,
prefix='project-%s' % (project.id,)
))
for project in sorted(project_list, key=lambda x: (x.team.name, x.name))

This comment has been minimized.

Copy link
@jbalogh

jbalogh Jul 19, 2013

My default project (created yesterday, running master) has team_id=None and owner_id=None, so this broke. Trying to figure out if I did something weird. Reading the code, it looks like that's expected for the default project.

This comment has been minimized.

Copy link
@dcramer

dcramer via email Jul 19, 2013

Author Member
]

ext_forms = []
for plugin in plugins.all():
for form in safe_execute(plugin.get_notification_forms) or ():
form = safe_execute(form, plugin, request.user, request.POST or None, prefix=plugin.slug)
if not form:
continue
forms.append(form)

# Ensure our form comes first
forms = [
NotificationSettingsForm(request.user, request.POST or None),
] + forms
ext_forms.append(form)

if request.POST:
if all(f.is_valid() for f in forms):
for form in forms:
all_forms = list(itertools.chain(
[settings_form], ext_forms, (f for _, f in project_forms)
))
if all(f.is_valid() for f in all_forms):
for form in all_forms:
form.save()
messages.add_message(request, messages.SUCCESS, 'Your settings were saved.')
return HttpResponseRedirect(request.path)

context = csrf(request)
context.update({
'forms': forms,
'settings_form': settings_form,
'project_forms': project_forms,
'ext_forms': ext_forms,
'page': 'notifications',
})
return render_to_response('sentry/account/notifications.html', context, request)
Expand Down

0 comments on commit c533ca3

Please sign in to comment.