summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVitor Freitas2019-01-13 13:32:17 +0200
committerVitor Freitas2019-01-13 13:32:17 +0200
commitb8a0cf965307989f2749554353b73adcca023a33 (patch)
treeed27af06d546e4f528f46599c80cd99f7de5a310
parent4544c0423b4c9ac316d48232a78d74e66ff0189b (diff)
downloadcolossus-b8a0cf965307989f2749554353b73adcca023a33.tar.gz
colossus-b8a0cf965307989f2749554353b73adcca023a33.tar.bz2
colossus-b8a0cf965307989f2749554353b73adcca023a33.zip
Add send to tag shortcut
-rw-r--r--colossus/apps/campaigns/forms.py44
-rw-r--r--colossus/apps/campaigns/tests/test_forms.py66
-rw-r--r--colossus/apps/campaigns/tests/test_views.py36
-rw-r--r--colossus/apps/campaigns/views.py11
-rw-r--r--colossus/apps/lists/templates/lists/tag_list.html2
5 files changed, 152 insertions, 7 deletions
diff --git a/colossus/apps/campaigns/forms.py b/colossus/apps/campaigns/forms.py
index 6d865c5..6f675cf 100644
--- a/colossus/apps/campaigns/forms.py
+++ b/colossus/apps/campaigns/forms.py
@@ -13,7 +13,49 @@ from .models import Campaign
class CreateCampaignForm(forms.ModelForm):
class Meta:
model = Campaign
- fields = ('name',)
+ fields = ('name', 'mailing_list', 'tag')
+ widgets = {
+ 'mailing_list': forms.HiddenInput(),
+ 'tag': forms.HiddenInput()
+ }
+
+ def save(self, commit=True) -> Campaign:
+ """
+ Before saving check the mailing_list and tag data for consistency
+ This is done during the save method instead of the clean because
+ both fields are hidden fields, used when the user clicks the "Send"
+ button on a tag (as a shortcut to create and campaign for subscribers
+ in a given tag).
+
+ If the mailing_list + tag combo is invalid there is nothing the user
+ can do in the user interface, so just fallback to create a campaign
+ with the Name only. Later on the user can set the mailing_list and tag.
+
+ :param commit: Boolean to indicate if the data should be saved
+ on the database or not
+ :return: A new Campaign instance
+ """
+
+ campaign = super().save(commit=False)
+ mailing_list = self.cleaned_data.get('mailing_list')
+ tag = self.cleaned_data.get('tag')
+
+ if tag is not None and mailing_list is None:
+ # Remove the tag if there was no mailing list associated with
+ # This is just to keep the consistency of the data
+ # In normal cases this should never happen, unless the user is injecting/forging
+ # a POST request manually
+ campaign.tag = None
+
+ if tag is not None and mailing_list is not None:
+ # Remove the tag if it is not associated with the mailing list
+ if not mailing_list.tags.filter(pk=tag.pk).exists():
+ campaign.tag = None
+
+ if commit:
+ campaign.save()
+
+ return campaign
class CampaignRecipientsForm(forms.ModelForm):
diff --git a/colossus/apps/campaigns/tests/test_forms.py b/colossus/apps/campaigns/tests/test_forms.py
new file mode 100644
index 0000000..bd67c9f
--- /dev/null
+++ b/colossus/apps/campaigns/tests/test_forms.py
@@ -0,0 +1,66 @@
+from colossus.apps.campaigns.forms import CreateCampaignForm
+from colossus.apps.campaigns.models import Campaign
+from colossus.apps.lists.tests.factories import MailingListFactory
+from colossus.apps.subscribers.tests.factories import TagFactory
+from colossus.test.testcases import TestCase
+
+
+class CreateCampaignFormTests(TestCase):
+ def setUp(self):
+ self.mailing_list_1 = MailingListFactory(name='list_1')
+ self.tag_1 = TagFactory(name='tag_1', mailing_list=self.mailing_list_1)
+ self.mailing_list_2 = MailingListFactory(name='list_2')
+ self.tag_2 = TagFactory(name='tag_2', mailing_list=self.mailing_list_2)
+
+ def test_campaign_creation(self):
+ form = CreateCampaignForm({'name': 'Test campaign'})
+ self.assertTrue(form.is_valid())
+ form.save()
+ self.assertTrue(Campaign.objects.filter(name='Test campaign').exists())
+
+ def test_form_validation(self):
+ form = CreateCampaignForm()
+ self.assertFalse(form.is_valid())
+
+ def test_campaign_with_list_and_tag(self):
+ form = CreateCampaignForm({
+ 'name': 'Test campaign',
+ 'mailing_list': self.mailing_list_1.pk,
+ 'tag': self.tag_1.pk
+ })
+ self.assertTrue(form.is_valid())
+ campaign = form.save()
+ self.assertEqual('Test campaign', campaign.name)
+ self.assertEqual('list_1', campaign.mailing_list.name)
+ self.assertEqual('tag_1', campaign.tag.name)
+
+ def test_campaign_with_list_and_invalid_tag(self):
+ """
+ The tag should belong to the mailing list
+ """
+ form = CreateCampaignForm({
+ 'name': 'Test campaign',
+ 'mailing_list': self.mailing_list_1.pk,
+ 'tag': self.tag_2.pk
+ })
+ self.assertTrue(form.is_valid())
+ campaign = form.save()
+ self.assertEqual('Test campaign', campaign.name)
+ self.assertEqual('list_1', campaign.mailing_list.name)
+ self.assertIsNone(campaign.tag)
+
+ def test_campaign_with_tag_and_without_list(self):
+ """
+ To include a tag the campaign should have a mailing list associated with
+ so we can validate if the tag belong to the mailing list
+ """
+ form = CreateCampaignForm({
+ 'name': 'Test campaign',
+ 'mailing_list': '',
+ 'tag': self.tag_1.pk
+ })
+ self.assertTrue(form.is_valid())
+ campaign = form.save()
+ self.assertEqual('Test campaign', campaign.name)
+ self.assertIsNone(campaign.mailing_list)
+ self.assertIsNone(campaign.tag)
diff --git a/colossus/apps/campaigns/tests/test_views.py b/colossus/apps/campaigns/tests/test_views.py
index e049f76..48f314b 100644
--- a/colossus/apps/campaigns/tests/test_views.py
+++ b/colossus/apps/campaigns/tests/test_views.py
@@ -1,8 +1,9 @@
from django.urls import reverse
-from colossus.apps.accounts.tests.factories import UserFactory
from colossus.apps.campaigns.models import Campaign
-from colossus.test.testcases import TestCase
+from colossus.apps.lists.tests.factories import MailingListFactory
+from colossus.apps.subscribers.tests.factories import TagFactory
+from colossus.test.testcases import AuthenticatedTestCase, TestCase
from .factories import CampaignFactory
@@ -40,16 +41,15 @@ class CampaignsLoginRequiredTests(TestCase):
self.assertRedirectsLoginRequired(response, url)
-class CampaignListViewSuccessTests(TestCase):
+class CampaignListViewSuccessTests(AuthenticatedTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
CampaignFactory.create_batch(5)
def setUp(self):
+ super().setUp()
self.campaigns = Campaign.objects.all()
- self.user = UserFactory(username='alex')
- self.client.login(username='alex', password='123')
self.url = reverse('campaigns:campaigns')
self.response = self.client.get(self.url)
@@ -66,3 +66,29 @@ class CampaignListViewSuccessTests(TestCase):
for content in contents:
with self.subTest(content=content):
self.assertContains(self.response, content)
+
+
+class CampaignCreateViewTests(AuthenticatedTestCase):
+ def setUp(self):
+ super().setUp()
+ self.mailing_list_1 = MailingListFactory(name='list_1')
+ self.tag_1 = TagFactory(name='tag_1', mailing_list=self.mailing_list_1)
+ self.url = reverse('campaigns:campaign_add')
+
+ def test_initial_data(self):
+ url = '{0}?mailing_list={1}&tag={2}'.format(self.url, self.mailing_list_1.pk, self.tag_1.pk)
+ response = self.client.get(url)
+ form = response.context['form']
+ self.assertEqual(form.initial['mailing_list'], self.mailing_list_1.pk)
+ self.assertEqual(form.initial['tag'], self.tag_1.pk)
+
+ def test_invalid_initial_data(self):
+ url = '{0}?mailing_list=xxx&tag=yyyy'.format(self.url)
+ response = self.client.get(url)
+ form = response.context['form']
+ self.assertEqual(form.initial, {})
+
+ def test_html(self):
+ response = self.client.get(self.url)
+ self.assertContains(response, '<input type="hidden"', 3) # csrf_token, mailing_list, tag
+ self.assertContains(response, '<input type="text"', 1) # name tag
diff --git a/colossus/apps/campaigns/views.py b/colossus/apps/campaigns/views.py
index 2d2b2fa..50986ee 100644
--- a/colossus/apps/campaigns/views.py
+++ b/colossus/apps/campaigns/views.py
@@ -71,6 +71,17 @@ class CampaignCreateView(CampaignMixin, CreateView):
model = Campaign
form_class = CreateCampaignForm
+ def get_initial(self):
+ initial = super().get_initial()
+ mailing_list_id = self.request.GET.get('mailing_list', '')
+ tag_id = self.request.GET.get('tag', '')
+ try:
+ initial['mailing_list'] = int(mailing_list_id)
+ initial['tag'] = int(tag_id)
+ except (TypeError, ValueError):
+ pass
+ return initial
+
@method_decorator(login_required, name='dispatch')
class CampaignEditView(CampaignMixin, DetailView):
diff --git a/colossus/apps/lists/templates/lists/tag_list.html b/colossus/apps/lists/templates/lists/tag_list.html
index 60e5383..35f7e7b 100644
--- a/colossus/apps/lists/templates/lists/tag_list.html
+++ b/colossus/apps/lists/templates/lists/tag_list.html
@@ -60,7 +60,7 @@
<span class="sr-only">{% trans 'Toggle Dropdown' %}</span>
</button>
<div class="dropdown-menu dropdown-menu-right">
- <a class="dropdown-item" href="#">{% trans 'Send' %}</a>
+ <a class="dropdown-item" href="{% url 'campaigns:campaign_add' %}?mailing_list={{ mailing_list.pk }}&tag={{ tag.pk }}">{% trans 'Send' %}</a>
<a class="dropdown-item" href="{% url 'lists:edit_tag' mailing_list.pk tag.pk %}">{% trans 'Edit' %}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url 'lists:delete_tag' mailing_list.pk tag.pk %}">{% trans 'Delete' %}</a>