From 2a6722200bea65bf6fac5fdb6e3f3af4e5020943 Mon Sep 17 00:00:00 2001 From: ramanaditya Date: Fri, 5 Mar 2021 22:37:16 +0530 Subject: [PATCH 1/3] Support for the multiple Email Clients --- colossus/apps/campaigns/api.py | 15 ++++++++------- colossus/apps/campaigns/forms.py | 4 ++-- colossus/apps/campaigns/views.py | 24 ++++++++++++++---------- colossus/utils.py | 32 +++++++++++++++++++++++++++++++- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/colossus/apps/campaigns/api.py b/colossus/apps/campaigns/api.py index c0028498..7949d0c4 100644 --- a/colossus/apps/campaigns/api.py +++ b/colossus/apps/campaigns/api.py @@ -2,16 +2,15 @@ import re from smtplib import SMTPException +import html2text from django.contrib.sites.shortcuts import get_current_site -from django.core.mail import EmailMultiAlternatives, get_connection +from django.core.mail import EmailMultiAlternatives from django.utils import timezone from django.utils.translation import gettext as _ -import html2text - from colossus.apps.campaigns.constants import CampaignStatus from colossus.apps.subscribers.constants import ActivityTypes -from colossus.utils import get_absolute_url +from colossus.utils import get_absolute_url, get_campaign_connection logger = logging.getLogger(__name__) @@ -100,7 +99,7 @@ def send_campaign_email_subscriber(email, subscriber, site, connection=None): return send_campaign_email(email, context, subscriber.get_email(), connection) -def send_campaign_email_test(email, recipient_list): +def send_campaign_email_test(email, recipient_list, connection=None): if email.campaign.mailing_list is not None: unsubscribe_absolute_url = get_absolute_url('subscribers:unsubscribe_manual', kwargs={ 'mailing_list_uuid': email.campaign.mailing_list.uuid @@ -108,10 +107,12 @@ def send_campaign_email_test(email, recipient_list): else: unsubscribe_absolute_url = '#' context = get_test_email_context(unsub=unsubscribe_absolute_url) - return send_campaign_email(email, context, recipient_list, is_test=True) + return send_campaign_email(email, context, recipient_list, is_test=True, connection=connection) def send_campaign(campaign): + connection = get_campaign_connection(campaign=campaign) + campaign.status = CampaignStatus.DELIVERING campaign.save(update_fields=['status']) site = get_current_site(request=None) # get site based on SITE_ID @@ -122,7 +123,7 @@ def send_campaign(campaign): if campaign.track_opens: campaign.email.enable_open_tracking() - with get_connection() as connection: + with connection: for subscriber in campaign.get_recipients(): if not subscriber.activities.filter(activity_type=ActivityTypes.SENT, email=campaign.email).exists(): sent = send_campaign_email_subscriber(campaign.email, subscriber, site, connection) diff --git a/colossus/apps/campaigns/forms.py b/colossus/apps/campaigns/forms.py index 3ec76663..2d3ea353 100644 --- a/colossus/apps/campaigns/forms.py +++ b/colossus/apps/campaigns/forms.py @@ -115,9 +115,9 @@ class CampaignTestEmailForm(forms.Form): class Meta: fields = ('email',) - def send(self, email): + def send(self, email, connection=None): recipient_email = self.cleaned_data.get('email') - send_campaign_email_test(email, recipient_email) + send_campaign_email_test(email, recipient_email, connection=connection) class EmailEditorForm(forms.Form): diff --git a/colossus/apps/campaigns/views.py b/colossus/apps/campaigns/views.py index a35923cc..f9679761 100644 --- a/colossus/apps/campaigns/views.py +++ b/colossus/apps/campaigns/views.py @@ -20,7 +20,6 @@ from colossus.apps.lists.models import MailingList from colossus.apps.subscribers.constants import ActivityTypes from colossus.apps.subscribers.models import Activity - from .api import get_test_email_context from .constants import CampaignStatus, CampaignTypes from .forms import ( @@ -29,6 +28,7 @@ ) from .mixins import CampaignMixin from .models import Campaign, Email, Link +from ...utils import get_campaign_connection @method_decorator(login_required, name='dispatch') @@ -173,16 +173,18 @@ def get_context_data(self, **kwargs): .count() subscriber_open_activities = Activity.objects \ - .filter(email__campaign_id=self.kwargs.get('pk'), activity_type=ActivityTypes.OPENED) \ - .values('subscriber__id', 'subscriber__email') \ - .annotate(total_opens=Count('id')) \ - .order_by('-total_opens')[:10] + .filter(email__campaign_id=self.kwargs.get('pk'), + activity_type=ActivityTypes.OPENED) \ + .values('subscriber__id', 'subscriber__email') \ + .annotate(total_opens=Count('id')) \ + .order_by('-total_opens')[:10] location_open_activities = Activity.objects \ - .filter(email__campaign_id=self.kwargs.get('pk'), activity_type=ActivityTypes.OPENED) \ - .values('location__country__code', 'location__country__name') \ - .annotate(total_opens=Count('id')) \ - .order_by('-total_opens')[:10] + .filter(email__campaign_id=self.kwargs.get('pk'), + activity_type=ActivityTypes.OPENED) \ + .values('location__country__code', 'location__country__name') \ + .annotate(total_opens=Count('id')) \ + .order_by('-total_opens')[:10] kwargs.update({ 'links': links, @@ -416,10 +418,12 @@ def campaign_edit_content(request, pk): @login_required def campaign_test_email(request, pk): campaign = get_object_or_404(Campaign, pk=pk) + connection = get_campaign_connection(campaign=campaign) + if request.method == 'POST': form = CampaignTestEmailForm(request.POST) if form.is_valid(): - form.send(campaign.email) + form.send(campaign.email, connection=connection) return redirect(campaign.get_absolute_url()) else: form = CampaignTestEmailForm() diff --git a/colossus/utils.py b/colossus/utils.py index 71a22baf..033fd256 100644 --- a/colossus/utils.py +++ b/colossus/utils.py @@ -3,11 +3,12 @@ from typing import Optional from django.conf import settings +from django.conf.global_settings import EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, EMAIL_HOST, EMAIL_PORT, EMAIL_USE_TLS from django.contrib.gis.geoip2 import GeoIP2 from django.contrib.sites.shortcuts import get_current_site +from django.core.mail import get_connection from django.http import HttpRequest from django.urls import reverse - from geoip2.errors import AddressNotFoundError from colossus.apps.core.models import City, Country @@ -102,3 +103,32 @@ def get_absolute_url(urlname: str, kwargs: dict = None) -> str: path = reverse(urlname, kwargs=kwargs) absolute_url = '%s://%s%s' % (protocol, site.domain, path) return absolute_url + + +def get_campaign_connection(campaign=None, *args, **kwargs): + if not campaign: + return get_connection() + smtp_username = campaign.mailing_list.smtp_username or EMAIL_HOST_USER + smtp_password = campaign.mailing_list.smtp_password or EMAIL_HOST_PASSWORD + smtp_host = campaign.mailing_list.smtp_host or EMAIL_HOST + smtp_port = campaign.mailing_list.smtp_port or EMAIL_PORT + use_tls = campaign.mailing_list.smtp_use_tls or EMAIL_USE_TLS + use_ssl = campaign.mailing_list.smtp_use_ssl + timeout = campaign.mailing_list.smtp_timeout + smtp_ssl_keyfile = campaign.mailing_list.smtp_ssl_keyfile + smtp_ssl_certfile = campaign.mailing_list.smtp_ssl_certfile + connection = get_connection(host=smtp_host, + port=smtp_port, + username=smtp_username, + password=smtp_password + ) + if use_tls: + connection.use_tls = use_tls + if timeout: + connection.timeout = timeout + if use_ssl and smtp_ssl_certfile and smtp_ssl_keyfile: + connection.use_ssl = use_ssl + connection.ssl_keyfile = smtp_ssl_keyfile + connection.ssl_certfile = smtp_ssl_certfile + + return connection From 57ae9f5449b8eec8f9c21e710e79133b8a468100 Mon Sep 17 00:00:00 2001 From: ramanaditya Date: Thu, 25 Mar 2021 22:03:13 +0530 Subject: [PATCH 2/3] reformatted with flake for CI build --- colossus/apps/campaigns/api.py | 3 ++- colossus/apps/campaigns/views.py | 27 +++++++++++++-------------- colossus/utils.py | 6 +++++- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/colossus/apps/campaigns/api.py b/colossus/apps/campaigns/api.py index 7949d0c4..e223c4c8 100644 --- a/colossus/apps/campaigns/api.py +++ b/colossus/apps/campaigns/api.py @@ -2,12 +2,13 @@ import re from smtplib import SMTPException -import html2text from django.contrib.sites.shortcuts import get_current_site from django.core.mail import EmailMultiAlternatives from django.utils import timezone from django.utils.translation import gettext as _ +import html2text + from colossus.apps.campaigns.constants import CampaignStatus from colossus.apps.subscribers.constants import ActivityTypes from colossus.utils import get_absolute_url, get_campaign_connection diff --git a/colossus/apps/campaigns/views.py b/colossus/apps/campaigns/views.py index f9679761..31ee894f 100644 --- a/colossus/apps/campaigns/views.py +++ b/colossus/apps/campaigns/views.py @@ -20,6 +20,8 @@ from colossus.apps.lists.models import MailingList from colossus.apps.subscribers.constants import ActivityTypes from colossus.apps.subscribers.models import Activity + +from ...utils import get_campaign_connection from .api import get_test_email_context from .constants import CampaignStatus, CampaignTypes from .forms import ( @@ -28,7 +30,6 @@ ) from .mixins import CampaignMixin from .models import Campaign, Email, Link -from ...utils import get_campaign_connection @method_decorator(login_required, name='dispatch') @@ -172,19 +173,17 @@ def get_context_data(self, **kwargs): .filter(campaign_id=self.kwargs.get('pk'), activity_type=ActivityTypes.UNSUBSCRIBED) \ .count() - subscriber_open_activities = Activity.objects \ - .filter(email__campaign_id=self.kwargs.get('pk'), - activity_type=ActivityTypes.OPENED) \ - .values('subscriber__id', 'subscriber__email') \ - .annotate(total_opens=Count('id')) \ - .order_by('-total_opens')[:10] - - location_open_activities = Activity.objects \ - .filter(email__campaign_id=self.kwargs.get('pk'), - activity_type=ActivityTypes.OPENED) \ - .values('location__country__code', 'location__country__name') \ - .annotate(total_opens=Count('id')) \ - .order_by('-total_opens')[:10] + subscriber_open_activities = Activity.objects.filter(email__campaign_id=self.kwargs.get('pk'), + activity_type=ActivityTypes.OPENED) \ + .values('subscriber__id', 'subscriber__email') \ + .annotate(total_opens=Count('id')) \ + .order_by('-total_opens')[:10] + + location_open_activities = Activity.objects.filter(email__campaign_id=self.kwargs.get('pk'), + activity_type=ActivityTypes.OPENED) \ + .values('location__country__code', 'location__country__name') \ + .annotate(total_opens=Count('id')) \ + .order_by('-total_opens')[:10] kwargs.update({ 'links': links, diff --git a/colossus/utils.py b/colossus/utils.py index 033fd256..01371826 100644 --- a/colossus/utils.py +++ b/colossus/utils.py @@ -3,12 +3,16 @@ from typing import Optional from django.conf import settings -from django.conf.global_settings import EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, EMAIL_HOST, EMAIL_PORT, EMAIL_USE_TLS +from django.conf.global_settings import ( + EMAIL_HOST, EMAIL_HOST_PASSWORD, EMAIL_HOST_USER, EMAIL_PORT, + EMAIL_USE_TLS, +) from django.contrib.gis.geoip2 import GeoIP2 from django.contrib.sites.shortcuts import get_current_site from django.core.mail import get_connection from django.http import HttpRequest from django.urls import reverse + from geoip2.errors import AddressNotFoundError from colossus.apps.core.models import City, Country From 11b34a216b2021a5da79cd6e347aef842f7b0c72 Mon Sep 17 00:00:00 2001 From: ramanaditya Date: Thu, 25 Mar 2021 22:41:32 +0530 Subject: [PATCH 3/3] CI Build fix --- colossus/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/colossus/urls.py b/colossus/urls.py index e49b97d8..89146a2b 100644 --- a/colossus/urls.py +++ b/colossus/urls.py @@ -20,9 +20,10 @@ if settings.DEBUG: - import debug_toolbar from django.conf.urls.static import static + import debug_toolbar + urlpatterns = [ path('__debug__/', include(debug_toolbar.urls)), ] + urlpatterns