Skip to content

Commit

Permalink
bug 859875 - watch page for email notification
Browse files Browse the repository at this point in the history
add django-tidings

update/copy/refactor wiki events code from SUMO

remove notifications from apps in use

restore and move watch view tests
  • Loading branch information
groovecoder committed Apr 25, 2013
1 parent f2f5ab1 commit f31022b
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 116 deletions.
5 changes: 3 additions & 2 deletions apps/devmo/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def i18n(request):


def next_url(request):
if 'login' not in request.path and 'register' not in request.path:
if (hasattr(request, 'path') and
'login' not in request.path and 'register' not in request.path):
return {'next_url': request.get_full_path()}
return {}
return {}
154 changes: 154 additions & 0 deletions apps/devmo/email_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import contextlib
import logging
from functools import wraps

from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.utils import translation

import jingo
import tower
from test_utils import RequestFactory


log = logging.getLogger('mdn.email')


@contextlib.contextmanager
def uselocale(locale):
"""Context manager for setting locale and returning
to previous locale.
This is useful for when doing translations for things run by
celery workers or out of the HTTP request handling path.
>>> with uselocale('xx'):
... subj = _('Subject of my email')
... msg = render_email(email_template, email_kwargs)
... mail.send_mail(subj, msg, ...)
...
In Kitsune, you can get the right locale from Profile.locale and
also request.LANGUAGE_CODE.
If Kitsune is handling an HTTP request already, you don't have to
run uselocale---the locale will already be set correctly.
"""
currlocale = translation.get_language()
tower.activate(locale)
yield
tower.activate(currlocale)


def safe_translation(f):
"""Call `f` which has first argument `locale`. If `f` raises an
exception indicative of a bad localization of a string, try again in
`settings.WIKI_DEFAULT_LANGUAGE`.
NB: This means `f` will be called up to two times!
"""
@wraps(f)
def wrapper(locale, *args, **kwargs):
try:
with uselocale(locale):
return f(locale, *args, **kwargs)
except (TypeError, KeyError, ValueError, IndexError) as e:
# Types of errors, and examples.
#
# TypeError: Not enough arguments for string
# '%s %s %s' % ('foo', 'bar')
# KeyError: Bad variable name
# '%(Foo)s' % {'foo': 10} or '{Foo}'.format(foo=10')
# ValueError: Incomplete Format, or bad format string.
# '%(foo)a' or '%(foo)' or '{foo'
# IndexError: Not enough arguments for .format() style string.
# '{0} {1}'.format(42)
log.error('Bad translation in locale "%s": %s', locale, e)

with uselocale(settings.WIKI_DEFAULT_LANGUAGE):
return f(settings.WIKI_DEFAULT_LANGUAGE, *args, **kwargs)

return wrapper


def render_email(template, context):
"""Renders a template in the currently set locale.
Falls back to WIKI_DEFAULT_LANGUAGE in case of error.
"""

@safe_translation
def _render(locale):
"""Render an email in the given locale.
Because of safe_translation decorator, if this fails,
the function will be run again in English.
"""
req = RequestFactory()
req.META = {}
req.locale = locale

return jingo.render_to_string(req, template, context)

return _render(translation.get_language())


def emails_with_users_and_watches(subject,
text_template,
html_template,
context_vars,
users_and_watches,
from_email=settings.TIDINGS_FROM_ADDRESS,
default_locale=settings.WIKI_DEFAULT_LANGUAGE,
**extra_kwargs):
"""Return iterable of EmailMessages with user and watch values substituted.
A convenience function for generating emails by repeatedly
rendering a Django template with the given ``context_vars`` plus a
``user`` and ``watches`` key for each pair in
``users_and_watches``
.. Note::
This is a locale-aware re-write of the same function in django-tidings.
It's kind of goofy--I ain't gonna lie.
:arg subject: lazy gettext subject string
:arg text_template: path to text template file
:arg html_template: path to html template file
:arg context_vars: a map which becomes the Context passed in to the
template and the subject string
:arg from_email: the from email address
:arg default_local: the local to default to if not user.profile.locale
:arg extra_kwargs: additional kwargs to pass into EmailMessage constructor
:returns: generator of EmailMessage objects
"""
@safe_translation
def _make_mail(locale, user, watch):
context_vars['user'] = user
context_vars['watch'] = watch[0]
context_vars['watches'] = watch

msg = EmailMultiAlternatives(
subject.format(**context_vars),
render_email(text_template, context_vars),
from_email,
[user.email],
**extra_kwargs)

if html_template:
msg.attach_alternative(
render_email(html_template, context_vars), 'text/html')

return msg

for u, w in users_and_watches:
if hasattr(u, 'profile'):
locale = u.profile.locale
else:
locale = default_locale

yield _make_mail(locale, u, w)
24 changes: 0 additions & 24 deletions apps/users/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
mock_post_mindtouch_user)

from dekicompat.backends import DekiUserBackend, MINDTOUCH_USER_XML
from notifications.tests import watch
from sumo.helpers import urlparams
from sumo.tests import TestCase, LocalizingClient
from sumo.urlresolvers import reverse
Expand Down Expand Up @@ -331,29 +330,6 @@ def test_new_user_activation(self, get_current):
user = User.objects.get(pk=user.pk)
assert user.is_active

@mock_put_mindtouch_user
@mock_post_mindtouch_user
@mock.patch_object(Site.objects, 'get_current')
def test_new_user_claim_watches(self, get_current):
"""Claim user watches upon activation."""
old, settings.CELERY_ALWAYS_EAGER = settings.CELERY_ALWAYS_EAGER, True

get_current.return_value.domain = 'su.mo.com'

watch(email='[email protected]', save=True)

now = time()
username = 'sumo%s' % now
user = RegistrationProfile.objects.create_inactive_user(
username, 'testpass', '[email protected]')
key = RegistrationProfile.objects.all()[0].activation_key
self.client.get(reverse('users.activate', args=[key]), follow=True)

# Watches are claimed.
assert user.watch_set.exists()

settings.CELERY_ALWAYS_EAGER = old

@mock_get_deki_user
def test_duplicate_username(self):
response = self.client.post(reverse('users.register'),
Expand Down
2 changes: 1 addition & 1 deletion apps/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import jingo

from access.decorators import logout_required, login_required
from notifications.tasks import claim_watches
from tidings.tasks import claim_watches
from sumo.decorators import ssl_required
from sumo.urlresolvers import reverse, split_path
from upload.tasks import _create_image_thumbnail
Expand Down
52 changes: 39 additions & 13 deletions apps/wiki/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,37 @@

from tower import ugettext as _

from notifications.events import InstanceEvent, Event
from devmo import email_utils
from tidings.events import InstanceEvent, Event
from sumo.urlresolvers import reverse
from wiki.helpers import diff_table
from wiki.models import Document


log = logging.getLogger('k.wiki.events')
log = logging.getLogger('mdn.wiki.events')


def context_dict(revision):
"""Return a dict that fills in the blanks in notification templates."""
document = revision.document
from_revision = revision.get_previous()
to_revision = revision
diff = diff_table(from_revision.content, to_revision.content,
from_revision.id, to_revision.id)

return {
'document_title': document.title,
'creator': revision.creator,
'host': Site.objects.get_current().domain,
'history_url': reverse('wiki.document_revisions',
locale=document.locale,
args=[document.slug]),

'diff': diff
}

def notification_mails(revision, subject, template, url, users_and_watches):
"""Return EmailMessages in the KB's standard notification mail format."""
"""Return EmailMessages in standard notification mail format."""
document = revision.document
subject = subject.format(title=document.title, creator=revision.creator,
locale=document.locale)
Expand All @@ -26,7 +47,7 @@ def notification_mails(revision, subject, template, url, users_and_watches):
'url': url,
'host': Site.objects.get_current().domain}
content = t.render(Context(c))
mail = EmailMessage(subject, content, settings.NOTIFICATIONS_FROM_ADDRESS)
mail = EmailMessage(subject, content, settings.TIDINGS_FROM_ADDRESS)

for u, dummy in users_and_watches:
mail.to = [u.email]
Expand All @@ -43,15 +64,20 @@ def __init__(self, revision):
self.revision = revision

def _mails(self, users_and_watches):
document = self.revision.document
# log.debug('Sending edited notification email for document (id=%s)' %
# document.id)
subject = _('{title} was edited by {creator}')
url = reverse('wiki.document_revisions', locale=document.locale,
args=[document.slug])
return notification_mails(self.revision, subject,
'wiki/email/edited.ltxt', url,
users_and_watches)
revision = self.revision
document = revision.document
log.debug('Sending edited notification email for document (id=%s)' %
document.id)
subject = _('{document_title} was edited by {creator}')
context = context_dict(revision)

return email_utils.emails_with_users_and_watches(
subject=subject,
text_template='wiki/email/edited.ltxt',
html_template=None,
context_vars=context,
users_and_watches=users_and_watches,
default_locale=document.locale)


class _RevisionInLocaleEvent(Event):
Expand Down
2 changes: 1 addition & 1 deletion apps/wiki/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import constance.config
from elasticutils.contrib.django.models import Indexable

from notifications.models import NotificationsMixin
from tidings.models import NotificationsMixin
from search.index import SearchMappingType, register_mapping_type
from search.tasks import register_live_index
from sumo import ProgrammingError
Expand Down
2 changes: 1 addition & 1 deletion apps/wiki/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def send_reviewed_notification(revision, document, message):
'message': message,
'url': url,
'host': Site.objects.get_current().domain}))
send_mail(subject, content, settings.NOTIFICATIONS_FROM_ADDRESS,
send_mail(subject, content, settings.TIDINGS_FROM_ADDRESS,
[revision.creator.email])


Expand Down
32 changes: 22 additions & 10 deletions apps/wiki/templates/wiki/email/edited.ltxt
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
{% load i18n %}
{# L10n: This is an email. Whitespace matters! #}
{% blocktrans %}
{{ creator }} created a new revision to the document
{{ document_title }}.

To view this document's history, click the following
link, or paste it into your browser's location bar:
{% endblocktrans %}
https://{{ host }}{{ url }}
{# This is an email. Whitespace matters! #}
{% from "includes/unsubscribe_text.ltxt" import unsubscribe_text with context %}
{% autoescape false %}
{% trans creator=creator, document_title=document_title %}
{{ creator }} created a new revision to the document {{ document_title }}.
{% endtrans %}


{% trans %}
To view this document's history, click the following link, or paste it
into your browser's location bar:
{% endtrans %}


https://{{ host }}{{ history_url }}

--
{# L10n: This is in an email. #}
{{ _('Changes:') }}
{{ diff|safe }}

{{ unsubscribe_text(watch) }}{% endautoescape %}
Loading

0 comments on commit f31022b

Please sign in to comment.