Merge remote-tracking branch 'origin/better-emails'
This commit is contained in:
commit
8c00eabedd
14 changed files with 290 additions and 113 deletions
|
@ -497,18 +497,20 @@ def confirm_user_email(code):
|
|||
user = code.user
|
||||
user.verified = True
|
||||
|
||||
old_email = None
|
||||
new_email = code.new_email
|
||||
if new_email:
|
||||
if find_user_by_email(new_email):
|
||||
raise DataModelException('E-mail address already used.')
|
||||
|
||||
old_email = user.email
|
||||
user.email = new_email
|
||||
|
||||
user.save()
|
||||
|
||||
code.delete_instance()
|
||||
|
||||
return user, new_email
|
||||
return user, new_email, old_email
|
||||
|
||||
|
||||
def create_reset_password_email_code(email):
|
||||
|
@ -551,6 +553,13 @@ def get_user(username):
|
|||
return None
|
||||
|
||||
|
||||
def get_user_or_org(username):
|
||||
try:
|
||||
return User.get(User.username == username, User.robot == False)
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
def get_user_or_org_by_customer_id(customer_id):
|
||||
try:
|
||||
return User.get(User.stripe_id == customer_id)
|
||||
|
|
45
emails/base.html
Normal file
45
emails/base.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>{{ subject }}</title>
|
||||
</head>
|
||||
<body bgcolor="#FFFFFF" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; margin: 0; padding: 0;"><style type="text/css">
|
||||
@media only screen and (max-width: 600px) {
|
||||
a[class="btn"] {
|
||||
display: block !important; margin-bottom: 10px !important; background-image: none !important; margin-right: 0 !important;
|
||||
}
|
||||
div[class="column"] {
|
||||
width: auto !important; float: none !important;
|
||||
}
|
||||
table.social div[class="column"] {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- HEADER -->
|
||||
<table class="head-wrap" bgcolor="#FFFFFF" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; width: 100%; margin: 0; padding: 0;"><tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"><td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"></td>
|
||||
<td class="header container" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; display: block !important; max-width: 100% !important; clear: both !important; margin: 0; padding: 0;">
|
||||
|
||||
<div class="content" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; max-width: 100%; display: block; margin: 0; padding: 15px;">
|
||||
<table bgcolor="#FFFFFF" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; width: 100%; margin: 0; padding: 0;"><tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"><td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"><img src="{{ app_logo }}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; max-width: 100%; margin: 0; padding: 0;" alt="{{ app_title }}" title="{{ app_title }}"/></td>
|
||||
</tr></table></div>
|
||||
|
||||
</td>
|
||||
<td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"></td>
|
||||
</tr></table><!-- /HEADER --><!-- BODY --><table class="body-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; width: 100%; margin: 0; padding: 0;"><tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"><td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"></td>
|
||||
<td class="container" bgcolor="#FFFFFF" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; display: block !important; max-width: 100% !important; clear: both !important; margin: 0; padding: 0;">
|
||||
|
||||
<div class="content" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; max-width: 100%; display: block; margin: 0; padding: 15px;">
|
||||
<table style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; width: 100%; margin: 0; padding: 0;"><tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"><td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;">
|
||||
{% block content %}{% endblock %}
|
||||
</td>
|
||||
</tr></table></div><!-- /content -->
|
||||
|
||||
</td>
|
||||
<td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; margin: 0; padding: 0;"></td>
|
||||
</tr></table><!-- /BODY -->
|
||||
</body>
|
||||
</html>
|
13
emails/changeemail.html
Normal file
13
emails/changeemail.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>E-mail Address Change Requested</h3>
|
||||
|
||||
This email address was recently asked to become the new e-mail address for user {{ username | user_reference }}.
|
||||
<br>
|
||||
<br>
|
||||
To confirm this change, please click the following link:<br>
|
||||
{{ app_link('confirm?code=' + token) }}
|
||||
|
||||
{% endblock %}
|
13
emails/confirmemail.html
Normal file
13
emails/confirmemail.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>Please Confirm E-mail Address</h3>
|
||||
|
||||
This email address was recently used to register user {{ username | user_reference }}.
|
||||
<br>
|
||||
<br>
|
||||
To confirm this email address, please click the following link:<br>
|
||||
{{ app_link('confirm?code=' + token) }}
|
||||
|
||||
{% endblock %}
|
12
emails/emailchanged.html
Normal file
12
emails/emailchanged.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>Account E-mail Address Changed</h3>
|
||||
|
||||
The email address for user {{ username | user_reference }} has been changed from this e-mail address to {{ new_email }}.
|
||||
<br>
|
||||
<br>
|
||||
If this change was not expected, please immediately log into your {{ username | admin_reference }} and reset your email address.
|
||||
|
||||
{% endblock %}
|
13
emails/passwordchanged.html
Normal file
13
emails/passwordchanged.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>Account Password Changed</h3>
|
||||
|
||||
The password for user {{ username | user_reference }} has been updated.
|
||||
<br>
|
||||
<br>
|
||||
If this change was not expected, please immediately log into your account settings and reset your email address,
|
||||
or <a href="https://quay.io/contact">contact support</a>.
|
||||
|
||||
{% endblock %}
|
13
emails/paymentfailure.html
Normal file
13
emails/paymentfailure.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>Subscription Payment Failure</h3>
|
||||
|
||||
Your recent payment for account {{ username | user_reference }} failed, which usually results in our payments processor canceling
|
||||
your subscription automatically. If you would like to continue to use {{ app_title }} without interruption,
|
||||
please add a new card to {{ app_title }} and re-subscribe to your plan.<br>
|
||||
<br>
|
||||
You can find the card and subscription management features under your {{ username | admin_reference }}<br>
|
||||
|
||||
{% endblock %}
|
18
emails/recovery.html
Normal file
18
emails/recovery.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>Account recovery</h3>
|
||||
|
||||
A user at {{ app_link() }} has attempted to recover their account
|
||||
using this email address.
|
||||
<br>
|
||||
<br>
|
||||
If you made this request, please click the following link to recover your account and
|
||||
change your password:
|
||||
{{ app_link('recovery?code=' + token) }}
|
||||
<br><br>
|
||||
If you did not make this request, your account has not been compromised and the user was
|
||||
not given access. Please disregard this email.
|
||||
|
||||
{% endblock %}
|
13
emails/repoauthorizeemail.html
Normal file
13
emails/repoauthorizeemail.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>Verify e-mail to receive repository notifications</h3>
|
||||
|
||||
A request has been made to send <a href="http://docs.quay.io/guides/notifications.html">notifications</a> to this email address for repository {{ (namespace, repository) | repository_reference }}
|
||||
|
||||
<br><br>
|
||||
To verify this email address, please click the following link:<br>
|
||||
{{ app_link('authrepoemail?code=' + token) }}
|
||||
|
||||
{% endblock %}
|
17
emails/teaminvite.html
Normal file
17
emails/teaminvite.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>Invitation to join team: {{ teamname }}</h3>
|
||||
|
||||
{{ inviter | user_reference }} has invited you to join the team <b>{{ teamname }}</b> under organization {{ organization | user_reference }}.
|
||||
|
||||
<br><br>
|
||||
|
||||
To join the team, please click the following link:<br>
|
||||
{{ app_link('confirminvite?code=' + token) }}
|
||||
|
||||
<br><br>
|
||||
If you were not expecting this invitation, you can ignore this email.
|
||||
|
||||
{% endblock %}
|
|
@ -21,7 +21,7 @@ from auth.permissions import (AdministerOrganizationPermission, CreateRepository
|
|||
from auth.auth_context import get_authenticated_user
|
||||
from auth import scopes
|
||||
from util.gravatar import compute_hash
|
||||
from util.useremails import (send_confirmation_email, send_recovery_email, send_change_email)
|
||||
from util.useremails import (send_confirmation_email, send_recovery_email, send_change_email, send_password_changed)
|
||||
from util.names import parse_single_urn
|
||||
|
||||
import features
|
||||
|
@ -168,6 +168,7 @@ class User(ApiResource):
|
|||
logger.debug('Changing password for user: %s', user.username)
|
||||
log_action('account_change_password', user.username)
|
||||
model.change_password(user, user_data['password'])
|
||||
send_password_changed(user.username, user.email)
|
||||
|
||||
if 'invoice_email' in user_data:
|
||||
logger.debug('Changing invoice_email for user: %s', user.username)
|
||||
|
|
|
@ -18,6 +18,7 @@ from endpoints.common import common_login, render_page_template, route_show_if,
|
|||
from endpoints.csrf import csrf_protect, generate_csrf_token
|
||||
from util.names import parse_repository_name
|
||||
from util.gravatar import compute_hash
|
||||
from util.useremails import send_email_changed
|
||||
from auth import scopes
|
||||
|
||||
import features
|
||||
|
@ -248,10 +249,13 @@ def confirm_email():
|
|||
new_email = None
|
||||
|
||||
try:
|
||||
user, new_email = model.confirm_user_email(code)
|
||||
user, new_email, old_email = model.confirm_user_email(code)
|
||||
except model.DataModelException as ex:
|
||||
return render_page_template('confirmerror.html', error_message=ex.message)
|
||||
|
||||
if new_email:
|
||||
send_email_changed(user.username, old_email, new_email)
|
||||
|
||||
common_login(user)
|
||||
|
||||
return redirect(url_for('web.user', tab='email')
|
||||
|
|
Binary file not shown.
|
@ -1,129 +1,151 @@
|
|||
from flask.ext.mail import Message
|
||||
|
||||
from app import mail, app, get_app_url
|
||||
from jinja2 import Template, Environment, FileSystemLoader, contextfilter
|
||||
from data import model
|
||||
from util.gravatar import compute_hash
|
||||
|
||||
def user_reference(username):
|
||||
user = model.get_user_or_org(username)
|
||||
if not user:
|
||||
return username
|
||||
|
||||
return """
|
||||
<span>
|
||||
<img src="http://www.gravatar.com/avatar/%s?s=16&d=identicon" style="vertical-align: middle; margin-left: 6px; margin-right: 4px;">
|
||||
<b>%s</b>
|
||||
</span>""" % (compute_hash(user.email), username)
|
||||
|
||||
|
||||
CONFIRM_MESSAGE = """
|
||||
This email address was recently used to register the username '%s'
|
||||
at <a href="%s">Quay.io</a>.<br>
|
||||
<br>
|
||||
To confirm this email address, please click the following link:<br>
|
||||
<a href="%s/confirm?code=%s">%s/confirm?code=%s</a>
|
||||
"""
|
||||
def repository_reference(pair):
|
||||
(namespace, repository) = pair
|
||||
|
||||
owner = model.get_user(namespace)
|
||||
if not owner:
|
||||
return "%s/%s" % (namespace, repository)
|
||||
|
||||
return """
|
||||
<span style="white-space: nowrap;">
|
||||
<img src="http://www.gravatar.com/avatar/%s?s=16&d=identicon" style="vertical-align: middle; margin-left: 6px; margin-right: 4px;">
|
||||
<a href="%s/repository/%s/%s">%s/%s</a>
|
||||
</span>
|
||||
""" % (compute_hash(owner.email), get_app_url(), namespace, repository, namespace, repository)
|
||||
|
||||
|
||||
CHANGE_MESSAGE = """
|
||||
This email address was recently asked to become the new e-mail address for username '%s'
|
||||
at <a href="%s">Quay.io</a>.<br>
|
||||
<br>
|
||||
To confirm this email address, please click the following link:<br>
|
||||
<a href="%s/confirm?code=%s">%s/confirm?code=%s</a>
|
||||
"""
|
||||
def admin_reference(username):
|
||||
user = model.get_user(username)
|
||||
if not user:
|
||||
return 'account settings'
|
||||
|
||||
if user.organization:
|
||||
return """
|
||||
<a href="%s/organization/%s/admin">organization's admin setting</a>
|
||||
""" % (get_app_url(), username)
|
||||
else:
|
||||
return """
|
||||
<a href="%s/user/">account settings</a>
|
||||
""" % (get_app_url())
|
||||
|
||||
|
||||
RECOVERY_MESSAGE = """
|
||||
A user at <a href="%s">Quay.io</a> has attempted to recover their account
|
||||
using this email address.<br>
|
||||
<br>
|
||||
If you made this request, please click the following link to recover your account and
|
||||
change your password:
|
||||
<a href="%s/recovery?code=%s">%s/recovery?code=%s</a><br>
|
||||
<br>
|
||||
If you did not make this request, your account has not been compromised and the user was
|
||||
not given access. Please disregard this email.<br>
|
||||
"""
|
||||
template_loader = FileSystemLoader(searchpath="emails")
|
||||
template_env = Environment(loader=template_loader)
|
||||
template_env.filters['user_reference'] = user_reference
|
||||
template_env.filters['admin_reference'] = admin_reference
|
||||
template_env.filters['repository_reference'] = repository_reference
|
||||
|
||||
|
||||
SUBSCRIPTION_CHANGE = """
|
||||
Change: {0}<br>
|
||||
Customer id: <a href="https://manage.stripe.com/customers/{1}">{1}</a><br>
|
||||
Customer email: <a href="mailto:{2}">{2}</a><br>
|
||||
Quay user or org name: {3}<br>
|
||||
"""
|
||||
def send_email(recipient, subject, template_file, parameters):
|
||||
app_title = app.config['REGISTRY_TITLE_SHORT']
|
||||
app_url = get_app_url()
|
||||
|
||||
def app_link_handler(url=None, title=None):
|
||||
real_url = app_url + '/' + url if url else app_url
|
||||
if not title:
|
||||
title = real_url if url else app_title
|
||||
|
||||
return '<a href="%s">%s</a>' % (real_url, title)
|
||||
|
||||
parameters.update({
|
||||
'subject': subject,
|
||||
'app_logo': 'https://quay.io/static/img/quay-logo.png', # TODO: make this pull from config
|
||||
'app_url': app_url,
|
||||
'app_title': app_title,
|
||||
'app_link': app_link_handler
|
||||
})
|
||||
|
||||
rendered_html = template_env.get_template(template_file + '.html').render(parameters)
|
||||
|
||||
msg = Message('[%s] %s' % (app_title, subject), sender='support@quay.io', recipients=[recipient])
|
||||
msg.html = rendered_html
|
||||
mail.send(msg)
|
||||
|
||||
|
||||
PAYMENT_FAILED = """
|
||||
Hi {0},<br>
|
||||
<br>
|
||||
Your recent payment for Quay.io failed, which usually results in our payments processorcanceling
|
||||
your subscription automatically. If you would like to continue to use Quay.io without interruption,
|
||||
please add a new card to Quay.io and re-subscribe to your plan.<br>
|
||||
<br>
|
||||
You can find the card and subscription management features under your account settings.<br>
|
||||
<br>
|
||||
Thanks and have a great day!<br>
|
||||
<br>
|
||||
-Quay.io Support<br>
|
||||
"""
|
||||
|
||||
|
||||
AUTH_FORREPO_MESSAGE = """
|
||||
A request has been made to send notifications to this email address for the
|
||||
<a href="%s">Quay.io</a> repository <a href="%s/repository/%s/%s">%s/%s</a>.
|
||||
<br>
|
||||
To confirm this email address, please click the following link:<br>
|
||||
<a href="%s/authrepoemail?code=%s">%s/authrepoemail?code=%s</a>
|
||||
"""
|
||||
|
||||
INVITE_TO_ORG_TEAM_MESSAGE = """
|
||||
Hi {0},<br>
|
||||
{1} has invited you to join the team <b>{2}</b> under organization <b>{3}</b> on <a href="{4}">{5}</a>.
|
||||
<br><br>
|
||||
To join the team, please click the following link:<br>
|
||||
<a href="{4}/confirminvite?code={6}">{4}/confirminvite?code={6}</a>
|
||||
<br><br>
|
||||
If you were not expecting this invitation, you can ignore this email.
|
||||
<br><br>
|
||||
Thanks,<br>
|
||||
- {5} Support
|
||||
"""
|
||||
|
||||
|
||||
SUBSCRIPTION_CHANGE_TITLE = 'Subscription Change - {0} {1}'
|
||||
def send_password_changed(username, email):
|
||||
send_email(email, 'Account password changed', 'passwordchanged', {
|
||||
'username': username
|
||||
})
|
||||
|
||||
def send_email_changed(username, old_email, new_email):
|
||||
send_email(old_email, 'Account e-mail address changed', 'emailchanged', {
|
||||
'username': username,
|
||||
'new_email': new_email
|
||||
})
|
||||
|
||||
def send_change_email(username, email, token):
|
||||
msg = Message('Quay.io email change. Please confirm your email.',
|
||||
sender='support@quay.io', # Why do I need this?
|
||||
recipients=[email])
|
||||
msg.html = CHANGE_MESSAGE % (username, get_app_url(), get_app_url(), token, get_app_url(), token)
|
||||
mail.send(msg)
|
||||
|
||||
send_email(email, 'E-mail address change requested', 'changeemail', {
|
||||
'username': username,
|
||||
'token': token
|
||||
})
|
||||
|
||||
def send_confirmation_email(username, email, token):
|
||||
msg = Message('Welcome to Quay.io! Please confirm your email.',
|
||||
sender='support@quay.io', # Why do I need this?
|
||||
recipients=[email])
|
||||
msg.html = CONFIRM_MESSAGE % (username, get_app_url(), get_app_url(), token, get_app_url(), token)
|
||||
mail.send(msg)
|
||||
|
||||
send_email(email, 'Please confirm your e-mail address', 'confirmemail', {
|
||||
'username': username,
|
||||
'token': token
|
||||
})
|
||||
|
||||
def send_repo_authorization_email(namespace, repository, email, token):
|
||||
msg = Message('Quay.io Notification: Please confirm your email.',
|
||||
sender='support@quay.io', # Why do I need this?
|
||||
recipients=[email])
|
||||
msg.html = AUTH_FORREPO_MESSAGE % (get_app_url(), get_app_url(), namespace, repository, namespace,
|
||||
repository, get_app_url(), token, get_app_url(), token)
|
||||
mail.send(msg)
|
||||
|
||||
subject = 'Please verify your e-mail address for repository %s/%s' % (namespace, repository)
|
||||
send_email(email, subject, 'repoauthorizeemail', {
|
||||
'namespace': namespace,
|
||||
'repository': repository,
|
||||
'token': token
|
||||
})
|
||||
|
||||
def send_recovery_email(email, token):
|
||||
msg = Message('Quay.io account recovery.',
|
||||
sender='support@quay.io', # Why do I need this?
|
||||
recipients=[email])
|
||||
msg.html = RECOVERY_MESSAGE % (get_app_url(), get_app_url(), token, get_app_url(), token)
|
||||
mail.send(msg)
|
||||
subject = 'Account recovery'
|
||||
send_email(email, subject, 'recovery', {
|
||||
'email': email,
|
||||
'token': token
|
||||
})
|
||||
|
||||
def send_payment_failed(email, username):
|
||||
send_email(email, 'Subscription Payment Failure', 'paymentfailure', {
|
||||
'username': username
|
||||
})
|
||||
|
||||
def send_org_invite_email(member_name, member_email, orgname, team, adder, code):
|
||||
send_email(member_email, 'Invitation to join team', 'teaminvite', {
|
||||
'inviter': adder,
|
||||
'token': code,
|
||||
'organization': orgname,
|
||||
'teamname': team
|
||||
})
|
||||
|
||||
|
||||
def send_invoice_email(email, contents):
|
||||
# Note: This completely generates the contents of the email, so we don't use the
|
||||
# normal template here.
|
||||
msg = Message('Quay.io payment received - Thank you!',
|
||||
sender='support@quay.io', # Why do I need this?
|
||||
sender='support@quay.io',
|
||||
recipients=[email])
|
||||
msg.html = contents
|
||||
mail.send(msg)
|
||||
|
||||
|
||||
# INTERNAL EMAILS BELOW
|
||||
|
||||
def send_subscription_change(change_description, customer_id, customer_email, quay_username):
|
||||
SUBSCRIPTION_CHANGE_TITLE = 'Subscription Change - {0} {1}'
|
||||
title = SUBSCRIPTION_CHANGE_TITLE.format(quay_username, change_description)
|
||||
msg = Message(title, sender='support@quay.io', recipients=['stripe@quay.io'])
|
||||
msg.html = SUBSCRIPTION_CHANGE.format(change_description, customer_id, customer_email,
|
||||
|
@ -131,19 +153,3 @@ def send_subscription_change(change_description, customer_id, customer_email, qu
|
|||
mail.send(msg)
|
||||
|
||||
|
||||
def send_payment_failed(customer_email, quay_username):
|
||||
msg = Message('Quay.io Subscription Payment Failure', sender='support@quay.io',
|
||||
recipients=[customer_email])
|
||||
msg.html = PAYMENT_FAILED.format(quay_username)
|
||||
mail.send(msg)
|
||||
|
||||
|
||||
def send_org_invite_email(member_name, member_email, orgname, team, adder, code):
|
||||
app_title = app.config['REGISTRY_TITLE_SHORT']
|
||||
app_url = get_app_url()
|
||||
|
||||
title = '%s has invited you to join a team in %s' % (adder, app_title)
|
||||
msg = Message(title, sender='support@quay.io', recipients=[member_email])
|
||||
msg.html = INVITE_TO_ORG_TEAM_MESSAGE.format(member_name, adder, team, orgname,
|
||||
app_url, app_title, code)
|
||||
mail.send(msg)
|
||||
|
|
Reference in a new issue