Holger Frey
7 years ago
16 changed files with 294 additions and 27 deletions
@ -0,0 +1,72 @@
@@ -0,0 +1,72 @@
|
||||
''' custom events and event subsribers ''' |
||||
|
||||
from pyramid.events import subscriber |
||||
from pyramid.renderers import render |
||||
from pyramid_mailer import get_mailer |
||||
from pyramid_mailer.message import Message |
||||
|
||||
|
||||
# custom events |
||||
|
||||
class UserNotification(object): |
||||
''' base class for user notification emails |
||||
|
||||
:param pyramid.request.Request request: current request object |
||||
:param ordr.models.account.Users account: send notification to this user |
||||
:param dict data: additional data to pass to the mail template |
||||
''' |
||||
|
||||
#: subject of the notification |
||||
subject = None |
||||
|
||||
#: template to render |
||||
template = None |
||||
|
||||
def __init__(self, request, account, data=None): |
||||
self.request = request |
||||
self.account = account |
||||
self.data = data |
||||
|
||||
|
||||
class ActivationNotification(UserNotification): |
||||
''' user notification for account activation ''' |
||||
subject = '[ordr] Your account was activated' |
||||
template = 'ordr:templates/emails/activation.jinja2' |
||||
|
||||
|
||||
class OrderStatusNotification(UserNotification): |
||||
''' user notification for order status change ''' |
||||
subject = '[ordr] Order Status Change' |
||||
template = 'ordr:templates/emails/order.jinja2' |
||||
|
||||
|
||||
class PasswordResetNotification(UserNotification): |
||||
''' user notification for password reset link ''' |
||||
subject = '[ordr] Password Reset' |
||||
template = 'ordr:templates/emails/password_reset.jinja2' |
||||
|
||||
|
||||
class RegistrationNotification(UserNotification): |
||||
''' user notification for account activation ''' |
||||
subject = '[ordr] Please verify your email address' |
||||
template = 'ordr:templates/emails/registration.jinja2' |
||||
|
||||
|
||||
# subsribers for events |
||||
|
||||
@subscriber(UserNotification) |
||||
def notify_user(event): |
||||
''' notify a user about an event ''' |
||||
body = render( |
||||
event.template, |
||||
{'user': event.account, 'data': event.data}, |
||||
event.request |
||||
) |
||||
message = Message( |
||||
subject=event.subject, |
||||
sender=event.request.registry.settings['mail.default_sender'], |
||||
recipients=[event.account.email], |
||||
html=body |
||||
) |
||||
mailer = get_mailer(event.request.registry) |
||||
mailer.send(message) |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
||||
<title>[ordr] verify your email address</title> |
||||
<link href='http://fonts.googleapis.com/css?family=Anton&subset=latin,latin-ext' rel='stylesheet' type='text/css'> |
||||
</head> |
||||
<body> |
||||
<h1>Hi there!</h1> |
||||
<p> |
||||
Please verify your email address for the account "{{ user.username }}" by following this link |
||||
<a href="{{ request.resource_url(context, data.token.hash) }}">{{ request.resource_url(context, data.token.hash) }}</a> |
||||
</p> |
||||
<p> The link will expire on {{ data.token.expires.strftime('%d.%m.%y at %H:%M') }}. |
||||
<p class="signature"> |
||||
Regards, |
||||
<br/> |
||||
<span class="brand">ordr</span> |
||||
</p> |
||||
<p class="footprint"> |
||||
<small>Please don't respont to this email! This is an automatically generated notification by the system.</small> |
||||
<a href="http://distractedbysquirrels.com/" target="_blank" title="This software was originally written by Sebastian Sebald." class="icon-dbs"></a> |
||||
</p> |
||||
</body> |
||||
</html> |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
''' Tests for ordr.events ''' |
||||
|
||||
from datetime import datetime |
||||
from pyramid.testing import DummyRequest |
||||
from pyramid_mailer import get_mailer |
||||
|
||||
from . import app_config, get_example_user # noqa: F401 |
||||
|
||||
|
||||
def test_user_notification_init(app_config): # noqa: F811 |
||||
''' test creation of user notification events ''' |
||||
from ordr.events import UserNotification |
||||
from ordr.models.account import Role |
||||
|
||||
user = get_example_user(Role.USER) |
||||
notification = UserNotification('request', user, 'data') |
||||
|
||||
assert notification.request == 'request' |
||||
assert notification.account == user |
||||
assert notification.data == 'data' |
||||
|
||||
|
||||
def test_notify_user(app_config): # noqa: F811 |
||||
from ordr.events import RegistrationNotification, notify_user |
||||
from ordr.models.account import Token, Role |
||||
|
||||
request = DummyRequest() |
||||
user = get_example_user(Role.USER) |
||||
token = Token(expires=datetime.utcnow(), hash='some_hash') |
||||
notification = RegistrationNotification(request, user, {'token': token}) |
||||
notify_user(notification) |
||||
mailer = get_mailer(request.registry) |
||||
last_mail = mailer.outbox[-1] |
||||
|
||||
assert 'Please verify your email address ' in last_mail.html |
||||
assert 'http://example.com//some_hash' in last_mail.html |
@ -1,12 +1,88 @@
@@ -1,12 +1,88 @@
|
||||
import pytest |
||||
import deform |
||||
|
||||
from pyramid.httpexceptions import HTTPFound |
||||
from pyramid.testing import DummyRequest |
||||
|
||||
from .. import app_config, dbsession # noqa: F401 |
||||
from .. import app_config, dbsession, get_post_request # noqa: F401 |
||||
|
||||
|
||||
def test_faq(): |
||||
REGISTRATION_FORM_DATA = { |
||||
'username': 'AmyMcDonald', |
||||
'first_name': 'Amy', |
||||
'last_name': 'Mc Donald', |
||||
'email': 'amy.mcdonald@example.com', |
||||
'__start__': 'password:mapping', |
||||
'password': 'Make Amy McDonald A Rich Girl Fund', |
||||
'password-confirm': 'Make Amy McDonald A Rich Girl Fund', |
||||
'__end__': 'password:mapping', |
||||
'create': 'create account' |
||||
} |
||||
|
||||
|
||||
def test_registration_form(): |
||||
from ordr.resources.account import RegistrationResource |
||||
from ordr.schemas.account import RegistrationSchema |
||||
from ordr.views.registration import registration_form |
||||
result = registration_form(None, None) |
||||
assert result == {} |
||||
|
||||
request = DummyRequest() |
||||
context = RegistrationResource(request=request, name=None, parent=None) |
||||
result = registration_form(context, None) |
||||
form = result['form'] |
||||
|
||||
assert isinstance(form, deform.Form) |
||||
assert isinstance(form.schema, RegistrationSchema) |
||||
|
||||
|
||||
def test_registration_form_valid(dbsession): # noqa: F811 |
||||
from ordr.models.account import User, Role, TokenSubject |
||||
from ordr.resources.account import RegistrationResource |
||||
from ordr.views.registration import registration_form_processing |
||||
|
||||
data = REGISTRATION_FORM_DATA.copy() |
||||
request = get_post_request(dbsession, data) |
||||
context = RegistrationResource(request=request, name=None, parent=None) |
||||
result = registration_form_processing(context, request) |
||||
|
||||
# return value of function call |
||||
assert isinstance(result, HTTPFound) |
||||
assert result.location == 'http://example.com/verify' |
||||
|
||||
# user should be added to database |
||||
user = dbsession.query(User).first() |
||||
assert user.username == data['username'] |
||||
assert user.first_name == data['first_name'] |
||||
assert user.last_name == data['last_name'] |
||||
assert user.email == data['email'] |
||||
assert user.check_password(data['password']) |
||||
assert user.role == Role.UNVALIDATED |
||||
|
||||
# a token should be created |
||||
token = user.tokens[0] |
||||
assert token.subject == TokenSubject.REGISTRATION |
||||
|
||||
# a verification email should be sent |
||||
# this is tested in the functional test since request.registry.notify |
||||
# doesn't know about event subscribers in the unittest |
||||
|
||||
|
||||
def test_registration_form_invalid(dbsession): # noqa: F811 |
||||
from ordr.views.registration import registration_form_processing |
||||
from ordr.resources.account import RegistrationResource |
||||
data = REGISTRATION_FORM_DATA.copy() |
||||
data['email'] = 'not an email address' |
||||
request = get_post_request(dbsession, data) |
||||
context = RegistrationResource(request=request, name=None, parent=None) |
||||
result = registration_form_processing(context, request) |
||||
assert result['form'].error is not None |
||||
|
||||
|
||||
def test_registration_form_no_create_button(dbsession): # noqa: F811 |
||||
from ordr.views.registration import registration_form_processing |
||||
from ordr.resources.account import RegistrationResource |
||||
data = REGISTRATION_FORM_DATA.copy() |
||||
data.pop('create') |
||||
request = get_post_request(dbsession, data) |
||||
context = RegistrationResource(request=request, name=None, parent=None) |
||||
result = registration_form_processing(context, request) |
||||
assert result.location == 'http://example.com//' |
||||
|
Reference in new issue