Skip to content

Commit

Permalink
Remove dependency on sendmail/msmtp. Use pyramid_mailer instead. INI …
Browse files Browse the repository at this point in the history
…now assumes the user will use a smtp service like mandrill
  • Loading branch information
mazz committed Dec 28, 2014
1 parent fd10c7f commit 5cc023c
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 40 deletions.
7 changes: 4 additions & 3 deletions initpyr.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def perform_installs():
subprocess.call(["bin/easy_install", "gunicorn"])
subprocess.call(["bin/easy_install", "redis"])
subprocess.call(["bin/easy_install", "wtforms"])
subprocess.call(["bin/easy_install", "pyramid_mailer"])
if options.database_type is "postgresql":
subprocess.call(["bin/easy_install", "psycopg2"])

Expand Down Expand Up @@ -193,15 +194,15 @@ def setup_dotini():
# Add template if it is in the yaml file
#if settings["template"] != None:

substitute_in_file(developmentini, "pyramid.includes =", "pyramid.includes =\n pyramid_mako")
substitute_in_file(productionini, "pyramid.includes =", "pyramid.includes =\n pyramid_mako")
substitute_in_file(developmentini, "pyramid.includes =", "pyramid.includes =\n pyramid_mailer\n pyramid_mako")
substitute_in_file(productionini, "pyramid.includes =", "pyramid.includes =\n pyramid_mailer\n pyramid_mako")

authsecret_orig = "sqlalchemy.url = sqlite:///%(here)s/" + options.project_name + ".sqlite"

if options.database_type is "postgresql":
authsecret_orig = "sqlalchemy.url = postgresql+psycopg2://PGUSERNAME:PGPASSWORD@localhost/" + options.project_name

authsecret_subst = authsecret_orig + "\n\nauth.secret=PLEASECHANGEME\n\nsession.secret = PLEASECHANGEMETOO\n\nemail.enable=true\n[email protected]\nemail.host=sendmail"
authsecret_subst = authsecret_orig + "\n\nauth.secret=PLEASECHANGEME\n\nsession.secret = PLEASECHANGEMETOO\n\nmail.host=smtp.mandrillapp.com\nmail.username=YOURMANDRILLAPPUSERNAME\nmail.password=YOURMANDRILLAPPPASSWORD\nmail.port=587\nmail.ssl=False\nmail.default_sender=donotreply@YOURDOMAIN"
substitute_in_file(developmentini, authsecret_orig, authsecret_subst)
substitute_in_file(productionini, authsecret_orig, authsecret_subst)
substitute_in_file(productionini, "[server:main]", "[server:main]\nunix_socket = %(here)s/" + unix_app_socket + "\nhost = localhost\nport = 80\n")
Expand Down
2 changes: 2 additions & 0 deletions initpyr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ maininitpy:
config.set_request_factory(RequestWithUserAttribute)
config = build_routes(config)
config.add_renderer('jsonp', JSONP(param_name='callback'))
config.registry['mailer'] = Mailer.from_settings(settings)
mainimports: |
from os.path import abspath
from os.path import dirname
Expand All @@ -42,6 +43,7 @@ maininitpy:
from pyramid.security import ALL_PERMISSIONS
from pyramid.session import SignedCookieSessionFactory
from pyramid_mailer.mailer import Mailer
class RootFactory(object):
__acl__ = [(Allow, Everyone, ALL_PERMISSIONS)]
Expand Down
186 changes: 186 additions & 0 deletions lib/msg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
"""Create and send messages to users
"""
import logging
import os
import smtplib

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from pyramid.settings import asbool
from pyramid_mailer.message import Message
import pyramid_mailer
from os import path
from pyramid.paster import bootstrap


LOG = logging.getLogger(__name__)

# notification statuses
# might have pending, sent, failed
MSG_STATUS = {
'pending': 0,
'sent': 1,
'failed': 2,
'not_sent': 3,
'error': 4,
}

app_settings = bootstrap(path.join(path.dirname(path.dirname(path.dirname(__file__))), "development.ini"))['registry'].settings

class Msg(object):
"""This is a base email message we can then tweak"""

def __init__(self, to, subject, settings):
"""Start up a basic message"""
self.to = to
self.subject = subject
self.settings = settings

self.from_addr = settings.get('email.from', None)

# need ot setup/override in the extending classes
self.message_file = None

def _get_message_body(self, template_file, message_data):
"""Return the completed message template body
"""
return "Test email message from bookie"
# lookup = config['pylons.app_globals'].mako_lookup
# template = lookup.get_template(template_file)

# # template vars are a combo of the obj dict and the extra dict
# template_vars = {'data': message_data}
# return template.render(**template_vars)

def send(self, message_data=None):
"""Send the message with the given subject
body can be sent as part of send() or it can be set to the object as
msg.body = "xxx"
"""

self.body = self._get_message_body(self.message_file, message_data)

msg = MIMEMultipart('related')
msg['Subject'] = self.subject
msg['From'] = self.from_addr

msg['To'] = self.to

plain_text = MIMEText(self.body, 'plain', _charset="UTF-8")
msg.attach(plain_text)

LOG.debug('msg: ' + repr(msg))

mailer = pyramid_mailer.mailer.Mailer.from_settings(app_settings)
message = Message(subject=msg['Subject'],
recipients=[msg['To']],
body=self.body)

mailer.send_immediately(message, fail_silently=False)
return MSG_STATUS['sent']

class ReactivateMsg(Msg):
"""Send an email for a reactivation email"""

def _get_message_body(self, template_file, message_data):
"""Return the completed message template body
"""
return """
Hello {username}:
Please activate your app account by clicking on the following url:
{url}
---
From Us""".format(**message_data)
# lookup = config['pylons.app_globals'].mako_lookup
# template = lookup.get_template(template_file)

# # template vars are a combo of the obj dict and the extra dict
# template_vars = {'data': message_data}
# return template.render(**template_vars)


class InvitationMsg(Msg):
"""Send an email that you've been invited to the system"""
def _get_message_body(self, template_file, message_data):
"""Return the completed message template body
"""
return """
You've been invited to The Site!
Please click the link below to activate your account.
{0}
---
From Us""".format(message_data)


class ImportFailureMessage(Msg):
"""Send an email that the import has failed."""

def _get_message_body(self, template_file, message_data):
"""Build the email message body."""

msg = """
The import for user {username} has failed to import. The path to the import
is:
{file_path}
Error:
{exc}
""".format(**message_data)
return msg


class UserImportFailureMessage(Msg):
"""Send an email to the user their import has failed."""

def _get_message_body(self, template_file, message_data):
"""Build the email message body."""

msg = """
Your import has failed. The error is listed below. Please file a bug at
https://github.com/mitechie/bookie/issues if this error continues. You may
also join #bookie on freenode irc if you wish to aid in debugging the issue.
If the error pertains to a specific bookmark in your import file you might try
removing it and importing the file again.
Error
----------
{exc}
A copy of this error has been logged and will be looked at.
---
From Us""".format(**message_data)
return msg


class UserImportSuccessMessage(Msg):
"""Send an email to the user after a successful import."""

def _get_message_body(self, template_file, message_data):
"""Build the email message body."""

msg = """
Your bookmark import is complete! We've begun processing your bookmarks to
load their page contents and fulltext index them. This process might take a
while if you have a large number of bookmarks. Check out your imported
bookmarks at https://bmark.us/{username}/recent.
---
From Us""".format(**message_data)
return msg
29 changes: 0 additions & 29 deletions msmtprc.sample

This file was deleted.

7 changes: 1 addition & 6 deletions queue/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
from ~~~PROJNAME~~~.queue.celery import celery
import transaction

#from celery import Celery

# from .celery import load_ini
# INI = load_ini()

import logging
LOG = logging.getLogger(__name__)

Expand All @@ -22,7 +17,7 @@ def email_signup_user(email, msg, settings, message_data):
:param iid: import id we need to pull and work on
"""
from ~~~PROJNAME~~~.lib.message import InvitationMsg
from ~~~PROJNAME~~~.lib.msg import InvitationMsg
msg = InvitationMsg(email, msg, settings)
status = msg.send(message_data)
if status == 4:
Expand Down
4 changes: 2 additions & 2 deletions views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from ~~~PROJNAME~~~.queue import tasks
from ~~~PROJNAME~~~.lib.access import api_auth
from ~~~PROJNAME~~~.lib.applog import AuthLog
from ~~~PROJNAME~~~.lib.message import ReactivateMsg
from ~~~PROJNAME~~~.lib.message import InvitationMsg
from ~~~PROJNAME~~~.lib.msg import ReactivateMsg
from ~~~PROJNAME~~~.lib.msg import InvitationMsg
#from ~~~PROJNAME~~~.lib.readable import ReadContent
#from ~~~PROJNAME~~~.lib.tagcommands import Commander

Expand Down

0 comments on commit 5cc023c

Please sign in to comment.