import os import json import logging from flask_mail import Message import features from _init import ROOT_DIR from app import mail, app, get_app_url from util.jinjautil import get_template_env from util.html import html2text logger = logging.getLogger(__name__) template_env = get_template_env(os.path.join(ROOT_DIR, "emails")) class CannotSendEmailException(Exception): pass class GmailAction(object): """ Represents an action that can be taken in Gmail in response to the email. """ def __init__(self, metadata): self.metadata = metadata @staticmethod def confirm(name, url, description): return GmailAction({ "@context": "http://schema.org", "@type": "EmailMessage", "action": { "@type": 'ConfirmAction', "name": name, "handler": { "@type": "HttpActionHandler", "url": get_app_url() + '/' + url } }, "description": description }) @staticmethod def view(name, url, description): return GmailAction({ "@context": "http://schema.org", "@type": "EmailMessage", "action": { "@type": 'ViewAction', "name": name, "url": get_app_url() + '/' + url }, "description": description }) def send_email(recipient, subject, template_file, parameters, action=None): app_title = app.config['REGISTRY_TITLE_SHORT'] app_url = get_app_url() html, plain = render_email(app_title, app_url, recipient, subject, template_file, parameters, action=None) msg = Message('[%s] %s' % (app_title, subject), recipients=[recipient]) msg.html = html msg.body = plain try: mail.send(msg) except Exception as ex: logger.exception('Error while trying to send email to %s', recipient) raise CannotSendEmailException(ex.message) def render_email(app_title, app_url, recipient, subject, template_file, parameters, action=None): def app_link_handler(url=None): return app_url + '/' + url if url else app_url app_logo = app.config.get('ENTERPRISE_LOGO_URL', 'https://quay.io/static/img/quay-logo.png') parameters.update({ 'subject': subject, 'app_logo': app_logo, 'app_url': app_url, 'app_title': app_title, 'hosted': features.BILLING, 'app_link': app_link_handler, 'action_metadata': json.dumps(action.metadata) if action else None, 'with_base_template': True, }) rendered_html = template_env.get_template(template_file + '.html').render(parameters) parameters.update({ 'with_base_template': False, }) rendered_for_plain = template_env.get_template(template_file + '.html').render(parameters) return rendered_html, html2text(rendered_for_plain) 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): send_email(email, 'E-mail address change requested', 'changeemail', { 'username': username, 'token': token }) def send_confirmation_email(username, email, token): action = GmailAction.confirm('Confirm E-mail', 'confirm?code=' + token, 'Verification of e-mail address') send_email(email, 'Please confirm your e-mail address', 'confirmemail', { 'username': username, 'token': token }, action=action) def send_repo_authorization_email(namespace, repository, email, token): action = GmailAction.confirm('Verify E-mail', 'authrepoemail?code=' + token, 'Verification of e-mail address') subject = 'Please verify your e-mail address for repository %s/%s' % (namespace, repository) send_email(email, subject, 'repoauthorizeemail', { 'namespace': namespace, 'repository': repository, 'token': token }, action=action) def send_org_recovery_email(org, admin_users): subject = 'Organization %s recovery' % (org.username) send_email(org.email, subject, 'orgrecovery', { 'organization': org.username, 'admin_usernames': [user.username for user in admin_users], }) def send_recovery_email(email, token): action = GmailAction.view('Recover Account', 'recovery?code=' + token, 'Recovery of an account') subject = 'Account recovery' send_email(email, subject, 'recovery', { 'email': email, 'token': token }, action=action) 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): action = GmailAction.view('Join %s' % team, 'confirminvite?code=' + code, 'Invitation to join a team') send_email(member_email, 'Invitation to join team', 'teaminvite', { 'inviter': adder, 'token': code, 'organization': orgname, 'teamname': team }, action=action) 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 payment received - Thank you!', recipients=[email]) msg.html = contents mail.send(msg) def send_logs_exported_email(email, export_id, status, exported_data_url=None, exported_data_expiration=None): send_email(email, 'Export Action Logs Complete', 'logsexported', { 'status': status, 'export_id': export_id, 'exported_data_url': exported_data_url, 'exported_data_expiration': exported_data_expiration, }) # INTERNAL EMAILS BELOW def send_subscription_change(change_description, customer_id, customer_email, quay_username): SUBSCRIPTION_CHANGE_TITLE = 'Subscription Change - {0} {1}' SUBSCRIPTION_CHANGE = """ Change: {0}
Customer id: {1}
Customer email: {2}
Quay user or org name: {3}
""" title = SUBSCRIPTION_CHANGE_TITLE.format(quay_username, change_description) msg = Message(title, recipients=['stripe@quay.io']) msg.html = SUBSCRIPTION_CHANGE.format(change_description, customer_id, customer_email, quay_username) mail.send(msg)