Skip to content
This repository has been archived by the owner on Oct 1, 2020. It is now read-only.

Commit

Permalink
add email settings to Site Configuration model
Browse files Browse the repository at this point in the history
 - allows staff users to override the email settings
 - settings can still be specified as env vars
  • Loading branch information
monty5811 committed Apr 24, 2016
1 parent 3ed4e1a commit 0929ca5
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 45 deletions.
15 changes: 8 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
DJANGO_SECRET_KEY=replace_me_with_a_long_random_string
DJANGO_EMAIL_HOST=smtp.mailgun.org # for example
# email settings (these can be set in the Site Configutation page):
DJANGO_EMAIL_HOST=smtp.mailgun.org
DJANGO_EMAIL_HOST_USER=postmaster@example.com
DJANGO_EMAIL_HOST_PASSWORD=password
DJANGO_FROM_EMAIL=apostello@example.com
ACCOUNT_DEFAULT_HTTP_PROTOCOL=http
#
DJANGO_TIME_ZONE="Europe/London"
# A user signing up from this domain will be approved after
# they verify their email address:
WHITELISTED_LOGIN_DOMAINS=# comma separated list of domains
ELVANTO_KEY= # you get this from elvanto, or use a dummy value
SLACK_URL='' # forward all messages here
FROM_EMAIL=noreply@example.com
COUNTRY_CODE=44 # only 44 has been tested. Only used for Elvanto imports
ACCOUNT_DEFAULT_HTTP_PROTOCOL=http

# only 44 has been tested. Only used for Elvanto imports:
COUNTRY_CODE=44
# DB settings
DATABASE_NAME= # fill in
DATABASE_USER= # fill in
Expand Down
16 changes: 3 additions & 13 deletions apostello/signals.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import logging

from django.core.mail import send_mail
from django.dispatch import receiver

from allauth.account.signals import email_confirmed, user_signed_up

logger = logging.getLogger('apostello')
from apostello.tasks import send_async_mail


@receiver(email_confirmed)
Expand Down Expand Up @@ -39,11 +35,5 @@ def email_admin_on_signup(request, user, **kwargs):
body = body.format(str(user))
from site_config.models import SiteConfiguration
to_ = SiteConfiguration.get_solo().office_email
try:
if len(to_) > 0:
send_mail("[apostello] New User", body, '', [to_], )
except Exception:
logger.error(
'Error sending email to office on new user sign up',
exc_info=True
)
if len(to_) > 0:
send_async_mail("[apostello] New User", body, [to_], )
16 changes: 13 additions & 3 deletions apostello/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import requests
from django.conf import settings
from django.core.mail import send_mail
from django.core.mail import get_connection, send_mail
from django.utils import timezone
from django_twilio.client import twilio_client
from twilio.rest.exceptions import TwilioRestException
Expand Down Expand Up @@ -136,8 +136,18 @@ def update_msgs_name(person_pk):

def send_async_mail(subject, body, to):
"""Send email."""
from_ = settings.EMAIL_FROM
send_mail(subject, body, from_, to)
# read email settings from DB, if they are empty, they will be read from
# settings.py instead
from site_config.models import SiteConfiguration
s = SiteConfiguration.get_solo()
conn = get_connection(
host=s.email_host or None,
port=s.email_port or None,
username=s.email_username or None,
password=s.email_password or None,
)
from_ = s.email_from or settings.EMAIL_FROM
send_mail(subject, body, from_, to, connection=conn)


def notify_office_mail(subject, body):
Expand Down
1 change: 0 additions & 1 deletion apostello/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ class NotApprovedView(TemplateView):

def get_context_data(self, **kwargs):
context = super(NotApprovedView, self).get_context_data(**kwargs)
from site_config. models import SiteConfiguration
s = SiteConfiguration.get_solo()
context['msg'] = s.not_approved_msg
return context
Expand Down
2 changes: 1 addition & 1 deletion docs/demo_site.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Demo Site

There is a demo site available at https://apostello-demo.herokuapp.com

You can login with the email `test@example.com` and the password `apostello`.
You can login with the email ``test@example.com`` and the password ``apostello``.

Note that:

Expand Down
10 changes: 5 additions & 5 deletions docs/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ A docker compose config is provided for easy local development.
Instructions:

1. Install docker and docker compose (or docker toolbox on windows)
2. Copy `.env.example` to `.env` and fill in the values. (You do not need the DB, RabbitMQ or opbeat values)
3. Run `docker-compose up`
4. In a separate terminal run `docker-compose run web ./manage.py migrate`
2. Copy ``.env.example`` to ``.env`` and fill in the values. (You do not need the DB, RabbitMQ or opbeat values)
3. Run ``docker-compose up``
4. In a separate terminal run ``docker-compose run web ./manage.py migrate``
5. Visit 127.0.0.1:4000
6. You can run django management commands by running `docker-compose run web ./manage.py createsuperuser`
6. You can run django management commands by running ``docker-compose run web ./manage.py createsuperuser``

The frontend assets can be found in `apostello/assets`, here you can see the different gulp tasks, etc.
The frontend assets can be found in ``apostello/assets``, here you can see the different gulp tasks, etc.
46 changes: 36 additions & 10 deletions docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ There are a number of ways you can deploy apostello:
Prerequisites
-------------

* *Required*: A domain name and a server (or you can use Heroku instead)
* *Required*: A `Twilio <https://www.twilio.com/>`_ account with a purchased number
* *Required*: An SMTP server or `Mailgun <https://www.mailgun.com/>`_, `Mandrill <https://mandrillapp.com/>`_, etc account for sending email notifications
* *Required*: A domain name and a server (or you can use Heroku instead).
* *Required*: A `Twilio <https://www.twilio.com/>`_ account with a purchased number.
* *Recommended*: An SMTP server or `Mailgun <https://www.mailgun.com/>`_, etc account for sending email notifications. You can setup apostello without this, but it will not be able to send emails. See :ref:`Email Setup <email-setup>` for more details.
* *Optional*: A web app registered for authentication with a Google account
* *Optional*: An `Elvanto <https://www.elvanto.com/r_Y7HXKNE6>`_ API Key for importing Elvanto groups
* *Optional*: An `Elvanto <https://www.elvanto.com/r_Y7HXKNE6>`_ API Key for importing Elvanto groups.
* *Optional*: An `opbeat <https://opbeat.com/>`_ account for error logging. You can setup opbeat logging on the front and back ends in separate opbeat apps: one for the django app and one for the js front end.

First Run
Expand All @@ -26,15 +26,15 @@ After you have successfully installed apostello there are a few more steps to fi

* Open your instance of apostello, you will be redirected to the login page
* Click the sign up button at the bottom to create a user account
* You should receive a confirmation email (if this does not work, there may be something wrong with your email settings)
* Once you have confirmed your email address, log in to your site. As the first user you will have been given admin privileges
* You should receive a confirmation email (if this does not work, there may be something wrong with your email settings, or you have not yet setup email sending - the first user to sign up is does not get verified so you will be able to log in, but you need to finish setting up the emails before anyone else will be able to sign up)
* Log in to your site. As the first user you will have been given admin privileges
* Open the `Tools` menu where you can edit the site configuration and some canned responses
* If you want to let users sign in with Google, then you need to follow the steps `here <https://django-allauth.readthedocs.org/en/stable/providers.html#google>`_
* If you need to approve new users, you will need to visit `/admin/auth/user/` in the admin panel
* If you need to approve new users, you will can use the User Permissions page under the tools menu
* Now you need to :ref:`setup Twilio <setup-twilio>`
* You may want to send yourself a test message to verify your setup
* You can now continue to set up apostello: import contacts, start sending messages, publicise your number, etc
* Any future users will be able to use the sign up page. If you do not whitelist any domains, you will need to approve new users before they can do anything
* Any future users will be able to use the sign up page. If you do not whitelist any domains, you will need to approve new users before they can do anything. Please be extremely careful with the whitelisting setting - if you set it to a domain that you have no control over (e.g ``gmail.com``), then anyone will be able to access your instance of apostello


.. _setup-twilio:
Expand All @@ -48,8 +48,34 @@ Once you have apostello setup we need to tell Twilio what url to talk to when it
* Click the number you are using in apostello and a popup should appear
* Click the "Messaging" tab if it is not already selected
* Select "Configure with URL"
* Ensure the HTTP method is set to `POST`
* In the "Request URL field" add the url to your server, followed by "/sms/". If you are using Heroku it may look like `https://apostello-demo.herokuapp.com/sms/` or if your site is hosted at `https://sms.example.com`, your URL would be `https://sms.example.com/sms`.
* Ensure the HTTP method is set to ``POST``
* In the "Request URL field" add the url to your server, followed by ``/sms/``. If you are using Heroku it may look like ``https://apostello-demo.herokuapp.com/sms/`` or if your site is hosted at ``https://sms.example.com``, your URL would be ``https://sms.example.com/sms``.
* Click save

Now you should be able to test your setup - send a message to your number and you should receive an automated reply. If not, raise an `issue <https://github.com/monty5811/apostello/issues/new?title=[Setup%20Help]>`_ or get in touch on `slack <http://chat.church.io>`_.

.. _email-setup:

Email Setup
-----------

Emails are sent for a number of reasons by apostello:

* Email verfication on sign up
* Daily keyword digests
* Warnings and notifications on some events are sent to the "office email"

apostello needs a mail server to send these emails.
We recommend using `Mailgun <https://www.mailgun.com/>`_ which allows you to send 10,000 emails for free each month.

There are two ways to tell apostello about your mail server:

1. Set environment variables. The relevant variables are:

* ``DJANGO_EMAIL_HOST``
* ``DJANGO_EMAIL_HOST_PORT``
* ``DJANGO_EMAIL_HOST_USER``
* ``DJANGO_EMAIL_HOST_PASSWORD``
* ``DJANGO_FROM_EMAIL``

2. Use the `Site Configuration` form after getting apostello up and running. **N.B.** These values will override those set as environment variables.
13 changes: 8 additions & 5 deletions settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,14 @@

# email settings
EMAIL_USE_TLS = True
EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST', 'smtp.mailgun.org')
EMAIL_HOST_USER = os.environ.get('DJANGO_EMAIL_HOST_USER', '')
EMAIL_HOST_PASSWORD = os.environ.get('DJANGO_EMAIL_HOST_PASSWORD', '')
EMAIL_FROM = os.environ.get('DJANGO_FROM_EMAIL')
EMAIL_PORT = 587
# these email settings can be overridden in the SiteConfiguration model
# this also allows for these settings to be left blank on initial setup and
# filled in later
EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST', None)
EMAIL_HOST_USER = os.environ.get('DJANGO_EMAIL_HOST_USER', None)
EMAIL_HOST_PASSWORD = os.environ.get('DJANGO_EMAIL_HOST_PASSWORD', None)
EMAIL_FROM = os.environ.get('DJANGO_FROM_EMAIL', None)
EMAIL_PORT = int(os.environ.get('DJANGO_EMAIL_HOST_port', 587))

# social login settings
ACCOUNT_AUTHENTICATION_METHOD = 'email'
Expand Down
5 changes: 5 additions & 0 deletions site_config/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ class SiteConfigurationForm(forms.ModelForm):
class Meta:
model = SiteConfiguration
exclude = []
widgets = {
'email_password': forms.PasswordInput(
render_value=True,
),
}


class DefaultResponsesForm(forms.ModelForm):
Expand Down
40 changes: 40 additions & 0 deletions site_config/migrations/0004_auto_20160422_0941.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-04-22 08:41
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('site_config', '0003_siteconfiguration_not_approved_msg'),
]

operations = [
migrations.AddField(
model_name='siteconfiguration',
name='email_from',
field=models.EmailField(blank=True, help_text='Email will be sent from this address. This will override values in settings.py', max_length=254),
),
migrations.AddField(
model_name='siteconfiguration',
name='email_host',
field=models.URLField(blank=True, help_text='Email host. This will override values in settings.py'),
),
migrations.AddField(
model_name='siteconfiguration',
name='email_password',
field=models.CharField(blank=True, help_text='Email password. This will override values in settings.py', max_length=255),
),
migrations.AddField(
model_name='siteconfiguration',
name='email_port',
field=models.PositiveIntegerField(blank=True, help_text='Email host port. This will override values in settings.py', null=True),
),
migrations.AddField(
model_name='siteconfiguration',
name='email_username',
field=models.CharField(blank=True, help_text='Email user name. This will override values in settings.py', max_length=255),
),
]
29 changes: 29 additions & 0 deletions site_config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,35 @@ class SiteConfiguration(SingletonModel):
'\n',
help_text='This message will be shown on the "not approved" page.',
)
# email sending settings
email_host = models.URLField(
blank=True,
help_text='Email host.'
' This will override values in settings.py',
)
email_port = models.PositiveIntegerField(
blank=True,
null=True,
help_text='Email host port.'
' This will override values in settings.py',
)
email_username = models.CharField(
blank=True,
max_length=255,
help_text='Email user name.'
' This will override values in settings.py',
)
email_password = models.CharField(
blank=True,
max_length=255,
help_text='Email password.'
' This will override values in settings.py',
)
email_from = models.EmailField(
blank=True,
help_text='Email will be sent from this address.'
' This will override values in settings.py',
)

def __str__(self):
"""Pretty representation."""
Expand Down

0 comments on commit 0929ca5

Please sign in to comment.