Add email backend
This commit is contained in:
parent
2ec9d2a630
commit
ebd1104efb
37
.editorconfig
Normal file
37
.editorconfig
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_size = 4
|
||||||
|
continuation_indent_size = 8
|
||||||
|
combine_as_imports = true
|
||||||
|
max_line_length = 88
|
||||||
|
multi_line_output = 4
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
|
[*.js]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.jsx]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.scss]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.tsx]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.html]
|
||||||
|
indent_size = 4
|
||||||
2
Pipfile
2
Pipfile
@ -14,6 +14,8 @@ django-localflavor = "*"
|
|||||||
django-extensions = "*"
|
django-extensions = "*"
|
||||||
markdown = "*"
|
markdown = "*"
|
||||||
pillow = "*"
|
pillow = "*"
|
||||||
|
django-anymail = {extras = ["mailgun"], version = "*"}
|
||||||
|
django-templated-email = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
|||||||
71
Pipfile.lock
generated
71
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "d96bc4c80d45b75316ad378ebf86a010b0fbbfe651b086caad0d70a037d515b2"
|
"sha256": "9590e03874968b489838ba3255c583c160e54a777547932a761d550b42ce46df"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -47,6 +47,22 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==5.2.7"
|
"version": "==5.2.7"
|
||||||
},
|
},
|
||||||
|
"certifi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
|
||||||
|
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==2022.6.15"
|
||||||
|
},
|
||||||
|
"charset-normalizer": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
|
||||||
|
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==2.1.0"
|
||||||
|
},
|
||||||
"click": {
|
"click": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||||
@ -60,7 +76,7 @@
|
|||||||
"sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667",
|
"sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667",
|
||||||
"sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"
|
"sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"
|
||||||
],
|
],
|
||||||
"markers": "python_full_version >= '3.6.2' and python_full_version < '4.0.0'",
|
"markers": "python_version < '4' and python_full_version >= '3.6.2'",
|
||||||
"version": "==0.3.0"
|
"version": "==0.3.0"
|
||||||
},
|
},
|
||||||
"click-plugins": {
|
"click-plugins": {
|
||||||
@ -85,6 +101,17 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==4.0.6"
|
"version": "==4.0.6"
|
||||||
},
|
},
|
||||||
|
"django-anymail": {
|
||||||
|
"extras": [
|
||||||
|
"mailgun"
|
||||||
|
],
|
||||||
|
"hashes": [
|
||||||
|
"sha256:49d83d7c16316ca86a624097496881d59b7d71b16bf1c5211cffa5b19ef98d0c",
|
||||||
|
"sha256:783342d49dd07d68778b81dd12a94c86e1d217463a68a85450a0513fabe31345"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==8.6"
|
||||||
|
},
|
||||||
"django-appconf": {
|
"django-appconf": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d",
|
"sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d",
|
||||||
@ -133,6 +160,30 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==3.1"
|
"version": "==3.1"
|
||||||
},
|
},
|
||||||
|
"django-render-block": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a01bfdb839e2f6b3f88a99021597484392bbd15d084f9a796e3e5658bae800f4",
|
||||||
|
"sha256:fbdd8be56cefcfd794756a2e62117cc031f9c5de3ef4bb53e9a3f877a359a1a7"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==0.9.1"
|
||||||
|
},
|
||||||
|
"django-templated-email": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:49d61840ec551e640adaf341146e94d6f9058ae01df964480850bf988046e5eb",
|
||||||
|
"sha256:bf1b68ffe6c8794c0c50e2ce20e3a166c6d511b3879abbd3cf059a3fc2fe2e60"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.0.0"
|
||||||
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
|
||||||
|
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.5'",
|
||||||
|
"version": "==3.3"
|
||||||
|
},
|
||||||
"kombu": {
|
"kombu": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610",
|
"sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610",
|
||||||
@ -268,6 +319,14 @@
|
|||||||
],
|
],
|
||||||
"version": "==1.1.0"
|
"version": "==1.1.0"
|
||||||
},
|
},
|
||||||
|
"requests": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
|
||||||
|
"sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7' and python_version < '4'",
|
||||||
|
"version": "==2.28.1"
|
||||||
|
},
|
||||||
"rjsmin": {
|
"rjsmin": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:05efa485dfddb6418e3b86d8862463aa15641a61f6ae05e7e6de8f116ee77c69",
|
"sha256:05efa485dfddb6418e3b86d8862463aa15641a61f6ae05e7e6de8f116ee77c69",
|
||||||
@ -309,6 +368,14 @@
|
|||||||
"markers": "python_version >= '3.5'",
|
"markers": "python_version >= '3.5'",
|
||||||
"version": "==0.4.2"
|
"version": "==0.4.2"
|
||||||
},
|
},
|
||||||
|
"urllib3": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
|
||||||
|
"sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
|
||||||
|
"version": "==1.26.10"
|
||||||
|
},
|
||||||
"vine": {
|
"vine": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30",
|
"sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30",
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.core.mail import send_mail
|
from django.contrib.sites.shortcuts import get_current_site
|
||||||
|
from templated_email import send_templated_mail
|
||||||
|
|
||||||
from .models import Post, Comment
|
from .models import Post, Comment
|
||||||
|
|
||||||
|
NOTIFICATION_EMAIL_TEMPLATE = 'notification.html'
|
||||||
|
|
||||||
|
|
||||||
class CommentCreateForm(forms.ModelForm):
|
class CommentCreateForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -22,20 +26,27 @@ class CommentCreateForm(forms.ModelForm):
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
def send_notification(self):
|
def send_notification(self, request):
|
||||||
# subject = self.cleaned_data['content_object']
|
|
||||||
author = self.instance.author
|
author = self.instance.author
|
||||||
message = self.cleaned_data.get('content')
|
message = self.cleaned_data.get('content')
|
||||||
post = Post.objects.get(pk=self.cleaned_data['object_id'])
|
post = Post.objects.get(pk=self.cleaned_data['object_id'])
|
||||||
|
url = get_current_site(request).domain + post.get_absolute_url()
|
||||||
recipients = list(post.recipients.all().values_list('email', flat=True))
|
recipients = list(post.recipients.all().values_list('email', flat=True))
|
||||||
if author in recipients:
|
if author in recipients:
|
||||||
recipients.remove(author.email)
|
recipients.remove(author.email)
|
||||||
|
|
||||||
send_mail(
|
context = {
|
||||||
post.title,
|
'subject': post.title,
|
||||||
message,
|
'author': author,
|
||||||
author.email,
|
'message': message,
|
||||||
recipients
|
'url': url
|
||||||
|
}
|
||||||
|
|
||||||
|
send_templated_mail(
|
||||||
|
template_name=NOTIFICATION_EMAIL_TEMPLATE,
|
||||||
|
from_email=author.email,
|
||||||
|
recipient_list=recipients,
|
||||||
|
context=context
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
20
src/core/migrations/0007_alter_post_recipients.py
Normal file
20
src/core/migrations/0007_alter_post_recipients.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 4.0.6 on 2022-07-21 00:19
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('core', '0006_alter_post_recipients'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='post',
|
||||||
|
name='recipients',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='subscriptions', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -130,7 +130,6 @@ class Post(models.Model):
|
|||||||
recipients = models.ManyToManyField(
|
recipients = models.ManyToManyField(
|
||||||
User,
|
User,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
|
||||||
related_name='subscriptions'
|
related_name='subscriptions'
|
||||||
)
|
)
|
||||||
tags = GenericRelation(Tag)
|
tags = GenericRelation(Tag)
|
||||||
|
|||||||
@ -45,7 +45,9 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<p>No posts</p>
|
<tr>
|
||||||
|
<td colspan="3">No posts</td>
|
||||||
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<li colspan="3">No topics yet</li>
|
<td colspan="3">No topics yet</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class CommentCreateView(LoginRequiredMixin, CreateView):
|
|||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.author = self.request.user
|
form.instance.author = self.request.user
|
||||||
form.send_notification()
|
form.send_notification(self.request)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
from dotenv import load_dotenv
|
|
||||||
import os
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
DEBUG = os.environ.get('DEBUG', 'True') == 'True'
|
||||||
|
|
||||||
DATABASE_CONFIG = {
|
DATABASE_CONFIG = {
|
||||||
'port': os.environ.get('DATABASE_PORT', ''),
|
'port': os.environ.get('DATABASE_PORT', ''),
|
||||||
'host': os.environ.get('DATABASE_HOST', ''),
|
'host': os.environ.get('DATABASE_HOST', ''),
|
||||||
@ -12,8 +14,19 @@ DATABASE_CONFIG={
|
|||||||
'engine': os.environ.get('DATABASE_ENGINE', ''),
|
'engine': os.environ.get('DATABASE_ENGINE', ''),
|
||||||
}
|
}
|
||||||
SECRET_KEY = os.environ.get('SECRET_KEY', '')
|
SECRET_KEY = os.environ.get('SECRET_KEY', '')
|
||||||
|
|
||||||
|
ANYMAIL_CONFIG = {
|
||||||
|
'MAILGUN_API_KEY': os.environ.get('MAILGUN_API_KEY', ''),
|
||||||
|
'MAILGUN_SENDER_DOMAIN': os.environ.get('MAILGUN_SENDER_DOMAIN', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFAULT_FROM_EMAIL = os.environ.get(
|
||||||
|
'DEFAULT_FROM_EMAIL', 'notifications@forum.windmillapps.org'
|
||||||
|
)
|
||||||
|
SERVER_EMAIL = os.environ.get('SERVER_EMAIL', 'server@forum.windmillapps.org')
|
||||||
|
ADMIN_EMAIL = os.environ.get('ADMIN_EMAIL', 'debug@nathanjchapman.com')
|
||||||
|
|
||||||
CACHE_CONFIG = {
|
CACHE_CONFIG = {
|
||||||
'location': os.environ.get('CACHE_LOCATION', ''),
|
'location': os.environ.get('CACHE_LOCATION', ''),
|
||||||
'backend': os.environ.get('CACHE_BACKEND', ''),
|
'backend': os.environ.get('CACHE_BACKEND', ''),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,9 +8,17 @@ from .config import *
|
|||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
# Add Your Required Allow Host
|
# Add Your Required Allow Host
|
||||||
ALLOWED_HOSTS = []
|
if not DEBUG:
|
||||||
|
ALLOWED_HOSTS = [
|
||||||
|
'forum.windmillapps.org',
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
ALLOWED_HOSTS = ['192.168.68.106', '127.0.0.1', 'localhost']
|
||||||
|
|
||||||
DEBUG = True
|
INTERNAL_IPS = [
|
||||||
|
'127.0.0.1',
|
||||||
|
'localhost',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
@ -22,10 +30,12 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'django.contrib.sites',
|
||||||
|
|
||||||
# 3rd party
|
# 3rd party
|
||||||
'django_filters',
|
'django_filters',
|
||||||
'compressor',
|
'compressor',
|
||||||
|
'anymail',
|
||||||
|
|
||||||
# Local
|
# Local
|
||||||
'accounts.apps.AccountsConfig',
|
'accounts.apps.AccountsConfig',
|
||||||
@ -42,6 +52,7 @@ MIDDLEWARE = [
|
|||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'forum.middleware.TimezoneMiddleware',
|
'forum.middleware.TimezoneMiddleware',
|
||||||
|
'django.contrib.sites.middleware.CurrentSiteMiddleware',
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -140,3 +151,17 @@ STATICFILES_FINDERS = (
|
|||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
AUTH_USER_MODEL = 'accounts.User'
|
AUTH_USER_MODEL = 'accounts.User'
|
||||||
LOGIN_REDIRECT_URL = reverse_lazy('core:topic-list')
|
LOGIN_REDIRECT_URL = reverse_lazy('core:topic-list')
|
||||||
|
|
||||||
|
# Email
|
||||||
|
ANYMAIL = ANYMAIL_CONFIG
|
||||||
|
EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend'
|
||||||
|
ADMINS = (
|
||||||
|
('Nathan Chapman', ADMIN_EMAIL),
|
||||||
|
)
|
||||||
|
|
||||||
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
TEMPLATED_EMAIL_BACKEND = 'templated_email.backends.vanilla_django.TemplateBackend'
|
||||||
|
|
||||||
|
# Site ID
|
||||||
|
SITE_ID = 1
|
||||||
|
|||||||
12
src/templates/templated_email/notification.html.email
Normal file
12
src/templates/templated_email/notification.html.email
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{% load helpers %}
|
||||||
|
|
||||||
|
{% block subject %}The Forum: {{ subject }}{% endblock %}
|
||||||
|
{% block html %}
|
||||||
|
<h3>{{ author }} posted:</h3>
|
||||||
|
|
||||||
|
{{ message|markdown|safe|truncatechars_html:64 }}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="{{ url }}">View or reply</a>
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
||||||
Loading…
x
Reference in New Issue
Block a user