diff --git a/.gitignore b/.gitignore index 0b1ce3e..e9c7f15 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.swp *.pyc __pycache__ +.tox/* diff --git a/.travis.yml b/.travis.yml index fdc91c6..3cfaeee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,11 @@ language: python python: - - 2.7 - 3.6 + - 3.8 env: - - DJANGO=1.8 - - DJANGO=1.9 - - DJANGO=1.10 - - DJANGO=1.11 - - DJANGO=2.0 + - DJANGO=2.2 + - DJANGO=3.2 + matrixs: exclude: - python: 3.6 diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..722d412 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,41 @@ +Change Log +========== + + +Under development +~~~~~~~~~~~~~~~~~~ +* + +2023.02.08 v05.9 +~~~~~~~~~~~~~~~~ +* Adds arabic translation; Mark strings are translatable. +* Adds check for change permission to send email from admin + +2022.07.19 v05.7 +~~~~~~~~~~~~~~~~ +* Adds `DB_EMAIL_FILTER_FUNCTION_PATH` setting to allow filtering out email from DB recording. +* Enhances in read me and strings translations. + +2022.07.18 v05.5 +~~~~~~~~~~~~~~~~ +* Fixes issues in case of error sending the mail. + +2022.04.21 v05.4 +~~~~~~~~~~~~~~~~ +* Adds `SMTP_EMAIL_FILTER_FUNCTION_PATH` setting to allow filtering out email from smtp sending. +* Enhance apps +* Rename has_error to succeeded for clearer more relaxed visual + + +2022.04.20 v05.1 +~~~~~~~~~~~~~~~~ +* Adds `SMTPDBEmailBackend` , a mixture between SMTP and DB mail backend. +* Remove south migrations +* Allow only superusers to access email body. +* Adds has_error, and error ro Email model + + +2021.11.29 +~~~~~~~~~~ +* Upgrade compatibility from the main fork to work with Django 3.2 + +* Add option on admin to (re)send the mail using the smtp backend \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index 64ad321..88a438e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ include README.md LICENSE +recursive-include db_email_backend/locale * \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index bde8387..0000000 --- a/README.md +++ /dev/null @@ -1,25 +0,0 @@ -Django DB Email Backend -======================= - -[![Build Status](https://travis-ci.org/jsatt/django-db-email-backend.svg?branch=master)](https://travis-ci.org/jsatt/django-db-email-backend) - -Django email backend for storing messages to a database. This is intended to be used in developement in cases where you want to test sending emails, but don't want to send real emails and don't have access to the console output (such as on a remote server). - -This is NOT intended for production use in any capacity. - -To install: - -```sh -pip install django-db-email-backend -``` - -In settings.py: - -```python -INSTALLED_APPS = [ - ... - 'db_email_backend', -] - -EMAIL_BACKEND = 'db_email_backend.backend.DBEmailBackend' -``` diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..71c6bcc --- /dev/null +++ b/README.rst @@ -0,0 +1,53 @@ +Django DB Email Backend +======================= + +Record Email Messages Sent to database , with the ability to also send them via SMTP. + + +Usage +----- + +Install :: + + pip install kn-django-db-email-backend + +In settings.py:: + + INSTALLED_APPS += ['db_email_backend'] + + EMAIL_BACKEND = 'db_email_backend.backend.DBEmailBackend' + # record the email message to database + + # OR + EMAIL_BACKEND = 'db_email_backend.backend.SMTPDBEmailBackend' + # Record to database and send via SMTP. + # IF errors happened you can see it in the Admin and resend it again. + +Migrate:: + + $ python manage.py migrate + + +Configuration +============= + +SMTP_EMAIL_FILTER_FUNCTION_PATH default to `db_email_backend.utils.smtp_filter_email_function`. a dotted path to the smtp email filter function. +A filter function for the smtp email, takes the email_message (django.core.mail.message.EmailMessage) as a parameter, and return False if this message should be filter out out and not sent via the backend. + +Example:: + + def only_allow_emails_to_mahad(message): + return True if 'mahad@kuwaitnet.com' in message.to else False + + +DB_EMAIL_FILTER_FUNCTION_PATH default to `db_email_backend.utils.db_filter_email_function`. a dotted path to the db email filter function. same as the SMTP one but for the Database. + +Example:: + + def dont_record_error_emails(message): + return False if settings.EMAIL_SUBJECT_PREFIX in message.subject else True + +Admin +----- + +Package have and admin integration ready where you can send the email SMTP even if the EMAIL_HOST = "db_email_backend.backend.DBEmailBackend" diff --git a/db_email_backend/__init__.py b/db_email_backend/__init__.py index 65e89da..baa2d30 100644 --- a/db_email_backend/__init__.py +++ b/db_email_backend/__init__.py @@ -1,2 +1,4 @@ -VERSION = (0, 4, 1) +default_app_config = 'db_email_backend.apps.DBEmailBackendConfig' + +VERSION = (0, 5, 9) __version__ = '.'.join(map(str, VERSION)) diff --git a/db_email_backend/admin.py b/db_email_backend/admin.py index befe601..d9502ec 100644 --- a/db_email_backend/admin.py +++ b/db_email_backend/admin.py @@ -2,6 +2,9 @@ from __future__ import unicode_literals from django.contrib import admin +from django.core.mail import EmailMessage +from django.test import override_settings +from django.utils.translation import gettext_lazy as _ from .models import Email, EmailAlternative, EmailAttachment @@ -26,26 +29,62 @@ class EmailAdmin(admin.ModelAdmin): fields = ( ('from_email', 'create_date', 'content_subtype'), ('to', 'cc', 'bcc'), - 'subject', 'body', 'headers') + 'subject', 'body', 'headers', 'succeeded', 'error') readonly_fields = ( - 'create_date','from_email', 'to', 'cc', 'bcc', 'subject', 'body', 'content_subtype', 'headers') - list_display = ('subject', 'to', 'from_email', 'create_date', 'attachment_count', 'alternative_count') - list_filter = ('content_subtype',) + 'create_date', 'from_email', 'to', 'cc', 'bcc', 'subject', 'body', 'content_subtype', 'headers', 'succeeded', + 'error') + list_display = ( + 'subject', 'to', 'from_email', 'create_date', 'attachment_count', 'alternative_count', 'succeeded', 'error') + list_filter = ('succeeded', 'content_subtype',) date_hierarchy = 'create_date' search_fields = ('to', 'from_email', 'cc', 'bcc', 'subject', 'body') inlines = (EmailAlternativeInline, EmailAttachmentInline) + actions = ['send_mail'] + + def get_actions(self, request): + actions = super().get_actions(request) + if not self.has_change_permission(request): + actions.pop('send_mail') + return actions + + def get_fields(self, request, obj=None): + fields = super().get_fields(request, obj) + if not request.user.is_superuser: + fields = list(fields) + fields.remove('body') + return fields + + @override_settings(EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend') + def send_mail(self, request, queryset): + for row in queryset: + msg = EmailMessage() + msg.subject = row.subject + msg.body = row.body + msg.content_subtype = row.content_subtype + msg.from_email = row.from_email + msg.to = [x for x in row.to.split('; ') if x] + msg.cc = [x for x in row.cc.split('; ') if x] + msg.bcc = [x for x in row.bcc.split('; ') if x] + + msg.send(fail_silently=False) + + self.message_user(request, f'{queryset.count()} Emails sent', 25) + + send_mail.short_description = _("Send Email") + def attachment_count(self, instance): return instance.attachments.count() - attachment_count.short_description = 'Attachments' + + attachment_count.short_description = _('Attachments') def alternative_count(self, instance): return instance.alternatives.count() - alternative_count.short_description = 'Alternatives' + + alternative_count.short_description = _('Alternatives') def has_add_permission(self, request, obj=None): return False -admin.site.register(Email, EmailAdmin) - +admin.site.register(Email, EmailAdmin) diff --git a/db_email_backend/app_settings.py b/db_email_backend/app_settings.py new file mode 100644 index 0000000..502c46f --- /dev/null +++ b/db_email_backend/app_settings.py @@ -0,0 +1,16 @@ +from django.conf import settings +from django.utils.module_loading import import_string + +SMTP_EMAIL_FILTER_FUNCTION_PATH = getattr(settings, 'SMTP_EMAIL_FILTER_FUNCTION_PATH', + 'db_email_backend.utils.smtp_filter_email_function') +DB_EMAIL_FILTER_FUNCTION_PATH = getattr(settings, 'SMTP_EMAIL_FILTER_FUNCTION_PATH', + 'db_email_backend.utils.db_filter_email_function') + +try: + email_filter = import_string(SMTP_EMAIL_FILTER_FUNCTION_PATH) +except Exception as e: + raise e +try: + db_email_filter = import_string(DB_EMAIL_FILTER_FUNCTION_PATH) +except Exception as e: + raise e diff --git a/db_email_backend/apps.py b/db_email_backend/apps.py new file mode 100644 index 0000000..ca9224f --- /dev/null +++ b/db_email_backend/apps.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class DBEmailBackendConfig(AppConfig): + name = 'db_email_backend' + verbose_name = _('Email Backend') diff --git a/db_email_backend/backend.py b/db_email_backend/backend.py index 008956f..2c57b67 100644 --- a/db_email_backend/backend.py +++ b/db_email_backend/backend.py @@ -1,43 +1,100 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import logging + from django.core.files.base import ContentFile from django.core.mail.backends.base import BaseEmailBackend +from django.core.mail.backends.smtp import EmailBackend as SMTPEmailBackend from .models import Email, EmailAlternative, EmailAttachment +from .app_settings import email_filter, db_email_filter + +logger = logging.getLogger('db_mail_backend') + + +def record_email_message(msg, fail_silently): + try: + email = Email.objects.create( + subject=msg.subject, + body=msg.body, + content_subtype=msg.content_subtype, + from_email=msg.from_email, + to='; '.join(msg.to), + cc='; '.join(msg.cc), + bcc='; '.join(msg.bcc), + headers='\n'.join('{}: {}'.format(k, v) + for k, v in msg.extra_headers.items()), + ) + # output.append(email) + + alternatives = getattr(msg, 'alternatives', []) + for content, mimetype in alternatives: + EmailAlternative.objects.create( + email=email, + content=content, + mimetype=mimetype or '', + ) + + for filename, content, mimetype in msg.attachments: + attachment = EmailAttachment.objects.create( + email=email, + filename=filename, + mimetype=mimetype or '', + ) + attachment.file.save(filename, ContentFile(content)) + except: + email = None + if not fail_silently: + raise + + return email + class DBEmailBackend(BaseEmailBackend): def send_messages(self, email_messages): for msg in email_messages: - try: - email = Email.objects.create( - subject=msg.subject, - body=msg.body, - content_subtype=msg.content_subtype, - from_email=msg.from_email, - to='; '.join(msg.to), - cc='; '.join(msg.cc), - bcc='; '.join(msg.bcc), - headers='\n'.join('{}: {}'.format(k, v) - for k, v in msg.extra_headers.items()), - ) - alternatives = getattr(msg, 'alternatives', []) - for content, mimetype in alternatives: - EmailAlternative.objects.create( - email=email, - content=content, - mimetype=mimetype or '', - ) - - for filename, content, mimetype in msg.attachments: - attachment = EmailAttachment.objects.create( - email=email, - filename=filename, - mimetype=mimetype or '', - ) - attachment.file.save(filename, ContentFile(content)) - except: - if not self.fail_silently: - raise - + if db_email_filter(msg): + return record_email_message(msg, fail_silently=self.fail_silently) return len(email_messages) + + +class SMTPDBEmailBackend(SMTPEmailBackend): + """ + This backend is mixture between SMTP and DB mail backend + It writes to the database then send the email over smtp, + if any errors happen while sending it is reflected in the email model + """ + + def send_messages(self, email_messages): + """ + Send one or more EmailMessage objects and return the number of email + messages sent. + """ + if not email_messages: + return 0 + with self._lock: + new_conn_created = self.open() + if not self.connection or new_conn_created is None: + # We failed silently on open(). + # Trying to send would be pointless. + return 0 + num_sent = 0 + for message in email_messages: + try: + if db_email_filter(message): + email_inst = record_email_message(message, fail_silently=self.fail_silently) + except Exception as e: + logger.error(e) + try: + if email_filter(message): + sent = self._send(message) + if sent: + num_sent += 1 + except Exception as e: + if email_inst: + Email.objects.filter(pk=email_inst.pk).update(error=str(e), succeeded=False) + raise e + if new_conn_created: + self.close() + return num_sent diff --git a/db_email_backend/locale/ar/LC_MESSAGES/django.mo b/db_email_backend/locale/ar/LC_MESSAGES/django.mo new file mode 100644 index 0000000..b1a901e Binary files /dev/null and b/db_email_backend/locale/ar/LC_MESSAGES/django.mo differ diff --git a/db_email_backend/locale/ar/LC_MESSAGES/django.po b/db_email_backend/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 0000000..724ba6a --- /dev/null +++ b/db_email_backend/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,69 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-02-08 12:18+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +msgid "Send Email" +msgstr "ارسل الايميل" + +msgid "Attachments" +msgstr "المرفقات" + +msgid "Alternatives" +msgstr "البدائل" + +msgid "Email Backend" +msgstr "خلفية البريد الإلكتروني" + +msgid "Creation date" +msgstr "تاريخ الإنشاء" + +msgid "Subject" +msgstr "موضوع" + +msgid "Body" +msgstr "نص" + +msgid "content subtype" +msgstr "نوع المحتوى الفرعي" + +msgid "From" +msgstr "من" + +msgid "To" +msgstr "الى" + +msgid "CC" +msgstr "" + +msgid "BCC" +msgstr "" + +msgid "Headers" +msgstr "" + +msgid "Succeeded?" +msgstr "نجاح؟" + +msgid "Errors" +msgstr "اخطاء" + +msgid "Email Log" +msgstr "سجل البريد الإلكتروني" + +msgid "Emails Log" +msgstr "سجل البريد الإلكتروني" diff --git a/db_email_backend/migrations/0002_auto_20220420_1246.py b/db_email_backend/migrations/0002_auto_20220420_1246.py new file mode 100644 index 0000000..bfe093a --- /dev/null +++ b/db_email_backend/migrations/0002_auto_20220420_1246.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.24 on 2022-04-20 09:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('db_email_backend', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='email', + name='error', + field=models.TextField(blank=True, verbose_name='Errors'), + ), + migrations.AddField( + model_name='email', + name='has_errors', + field=models.BooleanField(default=False, verbose_name='Has Errors'), + ), + ] diff --git a/db_email_backend/migrations/0003_auto_20220421_1249.py b/db_email_backend/migrations/0003_auto_20220421_1249.py new file mode 100644 index 0000000..5244dbf --- /dev/null +++ b/db_email_backend/migrations/0003_auto_20220421_1249.py @@ -0,0 +1,22 @@ +# Generated by Django 2.2.24 on 2022-04-21 09:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('db_email_backend', '0002_auto_20220420_1246'), + ] + + operations = [ + migrations.RemoveField( + model_name='email', + name='has_errors', + ), + migrations.AddField( + model_name='email', + name='succeeded', + field=models.BooleanField(default=True, verbose_name='Succeeded?'), + ), + ] diff --git a/db_email_backend/migrations/0004_auto_20250403_1558.py b/db_email_backend/migrations/0004_auto_20250403_1558.py new file mode 100644 index 0000000..87b9767 --- /dev/null +++ b/db_email_backend/migrations/0004_auto_20250403_1558.py @@ -0,0 +1,97 @@ +# Generated by Django 3.2.17 on 2025-04-03 12:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('db_email_backend', '0003_auto_20220421_1249'), + ] + + operations = [ + migrations.AlterModelOptions( + name='email', + options={'verbose_name': 'Email Log', 'verbose_name_plural': 'Emails Log'}, + ), + migrations.AlterField( + model_name='email', + name='bcc', + field=models.TextField(blank=True, verbose_name='BCC'), + ), + migrations.AlterField( + model_name='email', + name='body', + field=models.TextField(blank=True, verbose_name='Body'), + ), + migrations.AlterField( + model_name='email', + name='cc', + field=models.TextField(blank=True, verbose_name='CC'), + ), + migrations.AlterField( + model_name='email', + name='content_subtype', + field=models.CharField(max_length=254, verbose_name='content subtype'), + ), + migrations.AlterField( + model_name='email', + name='create_date', + field=models.DateTimeField(auto_now_add=True, verbose_name='Creation date'), + ), + migrations.AlterField( + model_name='email', + name='from_email', + field=models.CharField(blank=True, max_length=254, verbose_name='From'), + ), + migrations.AlterField( + model_name='email', + name='headers', + field=models.TextField(blank=True, verbose_name='Headers'), + ), + migrations.AlterField( + model_name='email', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='email', + name='subject', + field=models.TextField(blank=True, verbose_name='Subject'), + ), + migrations.AlterField( + model_name='email', + name='to', + field=models.TextField(blank=True, verbose_name='To'), + ), + migrations.AlterField( + model_name='emailalternative', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='emailattachment', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AddIndex( + model_name='email', + index=models.Index(fields=['create_date'], name='db_email_ba_create__e261e8_idx'), + ), + migrations.AddIndex( + model_name='email', + index=models.Index(fields=['from_email'], name='db_email_ba_from_em_237457_idx'), + ), + migrations.AddIndex( + model_name='email', + index=models.Index(fields=['to'], name='db_email_ba_to_ccc278_idx'), + ), + migrations.AddIndex( + model_name='email', + index=models.Index(fields=['cc'], name='db_email_ba_cc_f5ca2c_idx'), + ), + migrations.AddIndex( + model_name='email', + index=models.Index(fields=['bcc'], name='db_email_ba_bcc_aa743c_idx'), + ), + ] diff --git a/db_email_backend/models.py b/db_email_backend/models.py index dfa5e85..43c7f79 100644 --- a/db_email_backend/models.py +++ b/db_email_backend/models.py @@ -2,26 +2,30 @@ from __future__ import unicode_literals from django.db import models -from django.utils.encoding import python_2_unicode_compatible +from django.utils.translation import gettext_lazy as _ -@python_2_unicode_compatible class Email(models.Model): - create_date = models.DateTimeField(auto_now_add=True) - subject = models.TextField(blank=True) - body = models.TextField(blank=True) - content_subtype = models.CharField(max_length=254) - from_email = models.CharField(max_length=254, blank=True) - to = models.TextField(blank=True) - cc = models.TextField(blank=True) - bcc = models.TextField(blank=True) - headers = models.TextField(blank=True) + create_date = models.DateTimeField(auto_now_add=True, verbose_name=_("Creation date")) + subject = models.TextField(blank=True, verbose_name=_("Subject")) + body = models.TextField(blank=True, verbose_name=_('Body')) + content_subtype = models.CharField(max_length=254, verbose_name=_("content subtype")) + from_email = models.CharField(max_length=254, blank=True, verbose_name=_('From')) + to = models.TextField(blank=True, verbose_name=_('To')) + cc = models.TextField(blank=True, verbose_name=_('CC')) + bcc = models.TextField(blank=True, verbose_name=_('BCC')) + headers = models.TextField(blank=True, verbose_name=_('Headers')) + succeeded = models.BooleanField(default=True, verbose_name=_('Succeeded?')) + error = models.TextField(blank=True, verbose_name=_('Errors')) def __str__(self): return '{} - {}, {}'.format(self.subject, self.to, self.create_date) + class Meta: + verbose_name = _('Email Log') + verbose_name_plural = _('Emails Log') + -@python_2_unicode_compatible class EmailAlternative(models.Model): email = models.ForeignKey(Email, on_delete=models.CASCADE, related_name='alternatives') content = models.TextField(blank=True) @@ -31,7 +35,6 @@ def __str__(self): return '{}: alternative {}'.format(self.email, self.mimetype) -@python_2_unicode_compatible class EmailAttachment(models.Model): email = models.ForeignKey(Email, on_delete=models.CASCADE, related_name='attachments') filename = models.CharField(max_length=1000, blank=True) diff --git a/db_email_backend/south_migrations/0001_initial.py b/db_email_backend/south_migrations/0001_initial.py deleted file mode 100644 index 568a65f..0000000 --- a/db_email_backend/south_migrations/0001_initial.py +++ /dev/null @@ -1,88 +0,0 @@ -# -*- coding: utf-8 -*- -from south.utils import datetime_utils as datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'Email' - db.create_table(u'db_email_backend_email', ( - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('create_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - ('subject', self.gf('django.db.models.fields.TextField')(blank=True)), - ('body', self.gf('django.db.models.fields.TextField')(blank=True)), - ('content_subtype', self.gf('django.db.models.fields.CharField')(max_length=254)), - ('from_email', self.gf('django.db.models.fields.CharField')(max_length=254, blank=True)), - ('to', self.gf('django.db.models.fields.TextField')(blank=True)), - ('cc', self.gf('django.db.models.fields.TextField')(blank=True)), - ('bcc', self.gf('django.db.models.fields.TextField')(blank=True)), - ('headers', self.gf('django.db.models.fields.TextField')(blank=True)), - )) - db.send_create_signal(u'db_email_backend', ['Email']) - - # Adding model 'EmailAlternative' - db.create_table(u'db_email_backend_emailalternative', ( - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('email', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'alternatives', to=orm['db_email_backend.Email'])), - ('content', self.gf('django.db.models.fields.TextField')(blank=True)), - ('mimetype', self.gf('django.db.models.fields.CharField')(max_length=254, blank=True)), - )) - db.send_create_signal(u'db_email_backend', ['EmailAlternative']) - - # Adding model 'EmailAttachment' - db.create_table(u'db_email_backend_emailattachment', ( - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('email', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'attachments', to=orm['db_email_backend.Email'])), - ('filename', self.gf('django.db.models.fields.CharField')(max_length=1000, blank=True)), - ('mimetype', self.gf('django.db.models.fields.CharField')(max_length=254, blank=True)), - ('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)), - )) - db.send_create_signal(u'db_email_backend', ['EmailAttachment']) - - - def backwards(self, orm): - # Deleting model 'Email' - db.delete_table(u'db_email_backend_email') - - # Deleting model 'EmailAlternative' - db.delete_table(u'db_email_backend_emailalternative') - - # Deleting model 'EmailAttachment' - db.delete_table(u'db_email_backend_emailattachment') - - - models = { - u'db_email_backend.email': { - 'Meta': {'object_name': 'Email'}, - 'bcc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'body': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'cc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'content_subtype': ('django.db.models.fields.CharField', [], {'max_length': '254'}), - 'create_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'from_email': ('django.db.models.fields.CharField', [], {'max_length': '254', 'blank': 'True'}), - 'headers': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'subject': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'to': ('django.db.models.fields.TextField', [], {'blank': 'True'}) - }, - u'db_email_backend.emailalternative': { - 'Meta': {'object_name': 'EmailAlternative'}, - 'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'email': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'alternatives'", 'to': u"orm['db_email_backend.Email']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '254', 'blank': 'True'}) - }, - u'db_email_backend.emailattachment': { - 'Meta': {'object_name': 'EmailAttachment'}, - 'email': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'attachments'", 'to': u"orm['db_email_backend.Email']"}), - 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), - 'filename': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '254', 'blank': 'True'}) - } - } - - complete_apps = ['db_email_backend'] \ No newline at end of file diff --git a/db_email_backend/south_migrations/__init__.py b/db_email_backend/south_migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/db_email_backend/utils.py b/db_email_backend/utils.py new file mode 100644 index 0000000..8b3b37a --- /dev/null +++ b/db_email_backend/utils.py @@ -0,0 +1,15 @@ +def smtp_filter_email_function(message): + """ + A filter function for the smtp email... + if return False the backend won't send this message via smtp. + """ + return True + + +def db_filter_email_function(message): + """ + A filter function for the smtp email... + if return False the backend won't record this message to database. + """ + return True + diff --git a/setup.py b/setup.py index 42b2dcd..cb5bebd 100755 --- a/setup.py +++ b/setup.py @@ -11,16 +11,20 @@ os.system("python setup.py sdist upload") sys.exit() -long_description = open('README.md').read() +# long_description = open('README.md').read_text() +from pathlib import Path +this_directory = Path(__file__).parent +long_description = (this_directory / "README.rst").read_text() -setup_args = dict( - name='django-db-email-backend', +setup( + name='kn-django-db-email-backend', version=db_email_backend.__version__, description='Django email backend for storing messages to a database.', long_description=long_description, - author='Jeremy Satterfield', - author_email='jsatt@jsatt.com', - url='https://github.com/jsatt/django-db-email-backend', + long_description_content_type='text/x-rst', + author='Ramez Ashraf', + author_email='ramez@kuwaitnet.com', + url='https://github.com/KUWAITNET/django-db-email-backend', license="MIT License", classifiers=[ 'Development Status :: 4 - Beta', @@ -30,20 +34,15 @@ 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Topic :: Software Development', ], packages=[ 'db_email_backend', 'db_email_backend.migrations', - 'db_email_backend.south_migrations' ], install_requires=[ - "django>=1.8", + "django>=2.2", "pytz", ], ) - -if __name__ == '__main__': - setup(**setup_args) diff --git a/test_app/settings.py b/test_app/settings.py index 754cbd8..18adc41 100644 --- a/test_app/settings.py +++ b/test_app/settings.py @@ -1,5 +1,6 @@ import os import django + BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DEBUG = True @@ -53,7 +54,8 @@ 'OPTIONS': { 'context_processors': [ 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages' + 'django.contrib.messages.context_processors.messages', + 'django.template.context_processors.request' ] }, }, @@ -64,9 +66,4 @@ MEDIA_ROOT = BASE_DIR + '/media/' MEDIA_URL = '/media/' -try: - import south -except: - pass -else: - INSTALLED_APPS += ('south',) +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/tox.ini b/tox.ini index ca7535f..d50aaf8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,12 @@ [tox] envlist = - py{27,36}-django{18,19,110,111} - py{36}-django{20,21,22} + py{36}-django{32,22} + py{38}-django{32,22} [travis:env] DJANGO = - 1.8: django18 - 1.9: django19 - 1.10: django110 - 1.11: django111 - 2.0: django20 - 2.1: django21 2.2: django22 + 3.2: django32 [testenv] commands = django-admin.py test @@ -19,13 +14,8 @@ setenv = DJANGO_SETTINGS_MODULE=test_app.settings PYTHONPATH={toxinidir} deps = - django18: django>=1.8, <1.9 - django19: django>=1.9, <1.10 - django110: django>=1.10, <1.11 - django111: django>=1.11, <2.0 - django20: django>=2.0, <2.1 - django21: django>=2.1, <2.2 django22: django>=2.2, <2.3 + django32: django>=3.2, <4 [testenv:coverage] basepython=python3.6 @@ -34,4 +24,4 @@ commands = coveralls deps = coveralls - django>=2.0, <2.1 + django