wagtail-newsletter-django-backend 0.5.9__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,4 +3,4 @@
3
3
  This is a simple, basic internal backend for wagtail-newsletter, allowing you to manage your mailing lists internally and send them out through Django's standard email capabilities.
4
4
  '''
5
5
 
6
- __version__ = '0.5.9'
6
+ __version__ = '0.6.0'
@@ -5,7 +5,7 @@ from .models import Audience, AudienceSegment, Subscriber, Campaign
5
5
  @final
6
6
  class AudienceViewSet(ModelViewSet):
7
7
  model = Audience
8
- form_fields = ['site', 'name', 'description', 'smtp_user', 'smtp_password', 'from_email_address', 'from_email_name']
8
+ form_fields = ['site', 'name', 'description']
9
9
  list_display = ['site', 'name', 'description'] # pyright: ignore[reportAssignmentType]
10
10
  icon = 'group'
11
11
  add_to_admin_menu = False
@@ -35,7 +35,7 @@ class SubscriberViewSet(ModelViewSet):
35
35
  @final
36
36
  class CampaignViewSet(ModelViewSet):
37
37
  model = Campaign
38
- form_fields = ['subject', 'audience_segment', 'send_at', 'sent_at', 'html']
38
+ form_fields = ['subject', 'audience_segment', 'send_at', 'sent_at', 'html', 'from_email_address', 'from_email_name']
39
39
  list_display = ['subject', 'audience_segment'] # pyright: ignore[reportAssignmentType]
40
40
  icon = 'mail'
41
41
  add_to_admin_menu = False
@@ -1,3 +1,4 @@
1
+ from collections.abc import Iterable
1
2
  from datetime import datetime
2
3
  from typing import Any, cast, override
3
4
  from django.db import transaction
@@ -8,11 +9,12 @@ from html2text import HTML2Text
8
9
  from django.core.mail import get_connection
9
10
  from django.core.mail.message import EmailMessage, EmailMultiAlternatives
10
11
  from wagtail_newsletter import campaign_backends as wncb, audiences as wna, models as wnm
12
+ from wagtail.models import Site
11
13
  import re
12
14
  from . import models
13
15
 
14
- _unsubscribe_re = re.compile(r'\[\[unsubscribe\]\]|\\[\\[unsubscribe\\]\\]')
15
- _manage_re = re.compile(r'\[\[manage\]\]|\\[\\[manage\\]\\]')
16
+ _unsubscribe_re = re.compile(r'\[\[unsubscribe\]\]')
17
+ _manage_re = re.compile(r'\[\[manage\]\]')
16
18
 
17
19
  h2t = HTML2Text()
18
20
  class CampaignBackend(wncb.CampaignBackend):
@@ -27,7 +29,7 @@ class CampaignBackend(wncb.CampaignBackend):
27
29
  name=audience.name,
28
30
  member_count=audience.subscriber_set.filter(verified=True).count(),
29
31
  )
30
- for audience in models.Audience.objects.all()
32
+ for audience in cast(Iterable[models.Audience], models.Audience.objects.all())
31
33
  ]
32
34
 
33
35
  @override
@@ -35,7 +37,7 @@ class CampaignBackend(wncb.CampaignBackend):
35
37
  self,
36
38
  audience_id: str
37
39
  ) -> "list[wna.AudienceSegment]":
38
- audience = models.Audience.objects.get(id=int(audience_id))
40
+ audience = cast(models.Audience, models.Audience.objects.get(id=int(audience_id)))
39
41
  return [
40
42
  wna.AudienceSegment(
41
43
  id=f'{audience_id}/{audience_segment.id}',
@@ -43,7 +45,7 @@ class CampaignBackend(wncb.CampaignBackend):
43
45
  name=audience_segment.name,
44
46
  member_count=audience_segment.subscribers.filter(verified=True).count(),
45
47
  )
46
- for audience_segment in audience.audience_segment_set.all()
48
+ for audience_segment in cast(Iterable[models.AudienceSegment], audience.audience_segment_set.all())
47
49
  ]
48
50
 
49
51
  @override
@@ -54,37 +56,45 @@ class CampaignBackend(wncb.CampaignBackend):
54
56
  recipients: "wnm.NewsletterRecipientsBase | None",
55
57
  subject: str,
56
58
  html: str,
59
+ from_name: str,
60
+ reply_to: str,
57
61
  ) -> str:
58
62
  with transaction.atomic():
59
63
  audience_segment: models.AudienceSegment | None = None
60
64
  if recipients is not None and recipients.segment:
61
- audience_segment = models.AudienceSegment.objects.get(id=int(recipients.segment.split('/')[-1]))
65
+ audience_segment = cast(models.AudienceSegment, models.AudienceSegment.objects.get(id=int(recipients.segment.split('/')[-1])))
62
66
 
63
67
  campaign: models.Campaign | None
64
68
  if campaign_id:
65
69
  int_campaign_id = int(campaign_id)
66
- campaign = models.Campaign.objects.filter(id=int_campaign_id).first()
70
+ campaign = cast(models.Campaign | None, models.Campaign.objects.filter(id=int_campaign_id).first())
67
71
  if campaign is None:
68
72
  # No campaign exists with that ID anymore; recreate it.
69
- campaign = models.Campaign.objects.create(
73
+ campaign = cast(models.Campaign, models.Campaign.objects.create(
70
74
  id=int_campaign_id,
71
75
  subject=subject,
72
76
  html=html.strip(),
73
77
  audience_segment=audience_segment,
74
- )
78
+ from_email_address=reply_to,
79
+ from_email_name=from_name,
80
+ ))
75
81
  else:
76
82
  campaign.subject = subject
77
83
  campaign.html = html.strip()
84
+ campaign.from_email_address = reply_to
85
+ campaign.from_email_name = from_name
78
86
  if audience_segment is not None:
79
87
  campaign.audience_segment = audience_segment
80
88
  campaign.full_clean()
81
89
  campaign.save()
82
90
  else:
83
- campaign = models.Campaign.objects.create(
91
+ campaign = cast(models.Campaign, models.Campaign.objects.create(
84
92
  subject=subject,
85
93
  html=html.strip(),
86
94
  audience_segment=audience_segment,
87
- )
95
+ from_email_address=reply_to,
96
+ from_email_name=from_name,
97
+ ))
88
98
  return str(campaign.id)
89
99
 
90
100
  @override
@@ -93,17 +103,9 @@ class CampaignBackend(wncb.CampaignBackend):
93
103
 
94
104
  @override
95
105
  def send_test_email(self, *, campaign_id: str, email: str) -> None:
96
- campaign = models.Campaign.objects.get(id=int(campaign_id))
97
- audience: models.Audience
98
- if campaign.audience_segment is not None:
99
- audience = campaign.audience_segment.audience
100
- else:
101
- # Build empty audience. Will have no subscribers, and no smtp data.
102
- # This will not work without default data in the config.
103
- audience = models.Audience()
104
-
105
- from_address = audience.from_address()
106
- user, password = audience.smtp_auth()
106
+ campaign = cast(models.Campaign, models.Campaign.objects.get(id=int(campaign_id)))
107
+
108
+ from_address = campaign.from_address()
107
109
 
108
110
  _ = send_mail(
109
111
  subject=campaign.subject,
@@ -111,21 +113,19 @@ class CampaignBackend(wncb.CampaignBackend):
111
113
  from_email=from_address,
112
114
  recipient_list=[email],
113
115
  html_message=campaign.html,
114
- auth_user=user,
115
- auth_password=password,
116
116
  )
117
117
 
118
118
  @override
119
119
  def send_campaign(self, campaign_id: str) -> None:
120
120
  with transaction.atomic():
121
- campaign = models.Campaign.objects.get(id=int(campaign_id))
121
+ campaign = cast(models.Campaign, models.Campaign.objects.get(id=int(campaign_id)))
122
122
  if campaign.audience_segment is None:
123
123
  raise RuntimeError("Campaign can't be sent without an audience segment")
124
124
 
125
- audience = campaign.audience_segment.audience
125
+ audience = cast(models.Audience, campaign.audience_segment.audience)
126
126
 
127
- site = audience.site
128
- hostname = site.hostname
127
+ site = cast(Site, audience.site)
128
+ hostname = cast(str, site.hostname)
129
129
  match site.port:
130
130
  case 80:
131
131
  url_base = f'http://{hostname}'
@@ -134,13 +134,9 @@ class CampaignBackend(wncb.CampaignBackend):
134
134
  case port:
135
135
  url_base = f'http://{hostname}:{port}'
136
136
 
137
- from_address = audience.from_address()
138
- user, password = audience.smtp_auth()
137
+ from_address = campaign.from_address()
139
138
 
140
- connection: Any = get_connection( # pyright: ignore[reportAny]
141
- username=user,
142
- password=password,
143
- )
139
+ connection: Any = get_connection()
144
140
 
145
141
  def subscriber_message(subscriber: models.Subscriber) -> EmailMessage:
146
142
  unsubscribe_url = url_base + reverse('wagtail_newsletter_django_backend:newsletter_subscriber_unsubscribe', kwargs={'key': subscriber.key})
@@ -185,7 +181,7 @@ class CampaignBackend(wncb.CampaignBackend):
185
181
  @override
186
182
  def schedule_campaign(self, campaign_id: str, schedule_time: datetime) -> None:
187
183
  with transaction.atomic():
188
- campaign = models.Campaign.objects.get(id=int(campaign_id))
184
+ campaign = cast(models.Campaign, models.Campaign.objects.get(id=int(campaign_id)))
189
185
  campaign.send_at = schedule_time
190
186
  campaign.full_clean()
191
187
  campaign.save()
@@ -193,7 +189,7 @@ class CampaignBackend(wncb.CampaignBackend):
193
189
  @override
194
190
  def unschedule_campaign(self, campaign_id: str) -> None:
195
191
  with transaction.atomic():
196
- campaign = models.Campaign.objects.get(id=int(campaign_id))
192
+ campaign = cast(models.Campaign, models.Campaign.objects.get(id=int(campaign_id)))
197
193
  campaign.send_at = None
198
194
  campaign.full_clean()
199
195
  campaign.save()
@@ -0,0 +1,39 @@
1
+ # Generated by Django 5.2.5 on 2026-02-04 04:16
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('wagtail_newsletter_django_backend', '0003_alter_campaign_audience_segment'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name='audience',
15
+ name='from_email_address',
16
+ ),
17
+ migrations.RemoveField(
18
+ model_name='audience',
19
+ name='from_email_name',
20
+ ),
21
+ migrations.RemoveField(
22
+ model_name='audience',
23
+ name='smtp_password',
24
+ ),
25
+ migrations.RemoveField(
26
+ model_name='audience',
27
+ name='smtp_user',
28
+ ),
29
+ migrations.AddField(
30
+ model_name='campaign',
31
+ name='from_email_address',
32
+ field=models.EmailField(blank=True, default=None, help_text='The From address for the email. If not present, this will default to WAGTAIL_NEWSLETTER_REPLY_TO.', max_length=254, null=True),
33
+ ),
34
+ migrations.AddField(
35
+ model_name='campaign',
36
+ name='from_email_name',
37
+ field=models.CharField(blank=True, default=None, help_text='The display name portion of the from email. If not present, this will default to WAGTAIL_NEWSLETTER_FROM_NAME.', max_length=256, null=True),
38
+ ),
39
+ ]
@@ -15,16 +15,12 @@ import string
15
15
  from wagtail_newsletter import campaign_backends as wncb
16
16
 
17
17
  if TYPE_CHECKING:
18
- from django.db.models.manager import RelatedManager
18
+ from django.db.models.manager import RelatedManager, Manager
19
19
 
20
20
  @lru_cache
21
- def _default_from_parts() -> tuple[str | None, str | None]:
22
- email = cast(str | None, settings.DEFAULT_FROM_EMAIL)
23
- name: str | None = None
24
- if email:
25
- name, email = parseaddr(email)
26
- email = email or None
27
- name = name or None
21
+ def _default_from_parts() -> tuple[str | None, str]:
22
+ name = cast(str | None, getattr(settings, 'WAGTAIL_NEWSLETTER_FROM_NAME', None))
23
+ email = cast(str, settings.WAGTAIL_NEWSLETTER_REPLY_TO)
28
24
  return name, email
29
25
 
30
26
  class SaveDict(TypedDict):
@@ -56,33 +52,6 @@ class Audience(models.Model):
56
52
  site: 'models.ForeignKey[Site]' = models.ForeignKey(Site, blank=False, null=False, on_delete=models.CASCADE)
57
53
  name: 'models.CharField[str]' = models.CharField(max_length=64, blank=False, null=False)
58
54
  description: 'models.TextField[str]' = models.TextField(blank=True, null=False)
59
- smtp_user: 'models.CharField[str | None]' = models.CharField(
60
- max_length=256,
61
- blank=True,
62
- null=True,
63
- default=None,
64
- help_text='The SMTP login user. If absent, this will not be supplied to send_mail, and EMAIL_HOST_USER will be used instead',
65
- )
66
- smtp_password: 'models.CharField[str | None]' = models.CharField(
67
- max_length=256,
68
- blank=True,
69
- null=True,
70
- default=None,
71
- help_text='The SMTP login password. If absent, this will not be supplied to send_mail, and EMAIL_HOST_PASSWORD will be used instead',
72
- )
73
- from_email_address: 'models.EmailField[str | None]' = models.EmailField(
74
- blank=True,
75
- null=True,
76
- default=None,
77
- help_text='The From address for the email. If not present, this will not be supplied to send_mail, and DEFAULT_FROM_EMAIL will be used instead',
78
- )
79
- from_email_name: 'models.CharField[str | None]' = models.CharField(
80
- max_length=256,
81
- blank=True,
82
- null=True,
83
- default=None,
84
- help_text='The display name portion of the from email. This will be wrapped around the from email address.',
85
- )
86
55
 
87
56
  created_at: 'models.DateTimeField[datetime]' = models.DateTimeField(auto_now_add=True)
88
57
  updated_at: 'models.DateTimeField[datetime]' = models.DateTimeField(auto_now=True)
@@ -99,35 +68,6 @@ class Audience(models.Model):
99
68
  def __str__(self) -> str:
100
69
  return self.name
101
70
 
102
- _NULLABLE_FIELDS: Sequence[str] = ('smtp_user', 'smtp_password', 'from_email_address', 'from_email_name')
103
-
104
- @override
105
- def save(self, **kwargs: Unpack[SaveDict]) -> None: # pyright: ignore[reportIncompatibleMethodOverride]
106
- patch_blanks(self, kwargs.get('update_fields'), Audience._NULLABLE_FIELDS)
107
- return super().save(**kwargs)
108
-
109
- def from_address(self) -> str:
110
- default_from_name, default_from_email = _default_from_parts()
111
- from_email = self.from_email_address or default_from_email
112
- if from_email is None:
113
- raise RuntimeError('from email address must be set')
114
- from_name = self.from_email_name or default_from_name
115
- if from_name:
116
- return formataddr((from_name, from_email))
117
- else:
118
- return from_email
119
-
120
- def smtp_auth(self) -> tuple[str, str]:
121
- smtp_user = self.smtp_user or cast(str | None, settings.EMAIL_HOST_USER)
122
- smtp_password = self.smtp_password or cast(str | None, settings.EMAIL_HOST_PASSWORD)
123
- if smtp_user is None:
124
- raise RuntimeError('smtp_user must be set')
125
-
126
- if smtp_password is None:
127
- raise RuntimeError('smtp_password must be set')
128
-
129
- return smtp_user, smtp_password
130
-
131
71
  class AudienceSegment(models.Model):
132
72
  pk: int
133
73
  id: int # pyright: ignore[reportUninitializedInstanceVariable]
@@ -245,6 +185,8 @@ class Subscription(models.Model):
245
185
  raise ValidationError('The audience_segment and subscriber must have the same audience')
246
186
 
247
187
  class Campaign(models.Model):
188
+ objects: 'Manager[Campaign]'
189
+
248
190
  pk: int
249
191
  id: int # pyright: ignore[reportUninitializedInstanceVariable]
250
192
 
@@ -254,9 +196,40 @@ class Campaign(models.Model):
254
196
  html: 'models.TextField[str]' = models.TextField(blank=False, null=False)
255
197
  audience_segment: 'models.ForeignKey[AudienceSegment | None]' = models.ForeignKey(AudienceSegment, blank=False, null=True, on_delete=models.SET_NULL)
256
198
 
199
+ from_email_address: 'models.EmailField[str | None]' = models.EmailField(
200
+ blank=True,
201
+ null=True,
202
+ default=None,
203
+ help_text='The From address for the email. If not present, this will default to WAGTAIL_NEWSLETTER_REPLY_TO.',
204
+ )
205
+
206
+ from_email_name: 'models.CharField[str | None]' = models.CharField(
207
+ max_length=256,
208
+ blank=True,
209
+ null=True,
210
+ default=None,
211
+ help_text='The display name portion of the from email. If not present, this will default to WAGTAIL_NEWSLETTER_FROM_NAME.',
212
+ )
213
+
257
214
  created_at: 'models.DateTimeField[datetime]' = models.DateTimeField(auto_now_add=True)
258
215
  updated_at: 'models.DateTimeField[datetime]' = models.DateTimeField(auto_now=True)
259
216
 
217
+ _NULLABLE_FIELDS: Sequence[str] = ('from_email_address', 'from_email_name')
218
+
219
+ @override
220
+ def save(self, **kwargs: Unpack[SaveDict]) -> None: # pyright: ignore[reportIncompatibleMethodOverride]
221
+ patch_blanks(self, kwargs.get('update_fields'), Campaign._NULLABLE_FIELDS)
222
+ return super().save(**kwargs)
223
+
224
+ def from_address(self) -> str:
225
+ default_from_name, default_from_email = _default_from_parts()
226
+ from_email = cast(str | None, self.from_email_address) or default_from_email
227
+ from_name = cast(str | None, self.from_email_name) or default_from_name
228
+ if from_name:
229
+ return formataddr((from_name, from_email))
230
+ else:
231
+ return from_email
232
+
260
233
  @property
261
234
  def is_sent(self) -> bool:
262
235
  return self.sent_at is not None
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wagtail-newsletter-django-backend
3
- Version: 0.5.9
3
+ Version: 0.6.0
4
4
  Summary: An internal Django backend to wagtail-newsletter.
5
5
  Author-email: "Taylor C. Richberger" <taylor@axfive.net>
6
6
  Maintainer-email: "Taylor C. Richberger" <taylor@axfive.net>
7
7
  Requires-Python: >= 3.10
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
- Requires-Dist: Django >=5.2, <5.3
11
- Requires-Dist: wagtail >=7.1, <7.3
10
+ Requires-Dist: Django >=5.2, <6.1
11
+ Requires-Dist: wagtail >=7.1, <7.4
12
12
  Requires-Dist: wagtail-newsletter >=0.2.2, <0.2.4
13
13
  Requires-Dist: html2text >= 2025.4.15
14
14
  Project-URL: documentation, https://wagtail-newsletter-django-backend.readthedocs.io/en/latest/
@@ -1,10 +1,10 @@
1
- wagtail_newsletter_django_backend/__init__.py,sha256=6SmFvenfY_K9n4qjSpFkHf3U9ZbQwRxwWCtNmUKXR-c,263
1
+ wagtail_newsletter_django_backend/__init__.py,sha256=F-EYfMivbkMeKdUEr5EYTmH61gfTLfwdTzKnKOQX4oc,263
2
2
  wagtail_newsletter_django_backend/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
3
- wagtail_newsletter_django_backend/admin_viewsets.py,sha256=FBiRwMT0vwvxSrNgEVy6UYeqKZDn8LJczSSnjedHDz0,2035
3
+ wagtail_newsletter_django_backend/admin_viewsets.py,sha256=sNDju_ljRNiZB5AsmU7BO58bXrkVZgEYq3m0WAnz2f8,2005
4
4
  wagtail_newsletter_django_backend/apps.py,sha256=fFm2QZzxZlpYBQTNRKzBXpzOUzjK5xbW2xCQdv5fNOA,281
5
- wagtail_newsletter_django_backend/campaign_backend.py,sha256=Vp8ZnHwJp-gGOxlRb_lJsBywEsFmO450FEjBnQHoqrQ,8035
5
+ wagtail_newsletter_django_backend/campaign_backend.py,sha256=usVi38L2zoVxxHeH-cwDqICY64DueZ0m-xncDfDH1QE,8148
6
6
  wagtail_newsletter_django_backend/forms.py,sha256=5UK_-Fd7KuOtRkPZW_uyHM9D6BU5_JANKcM3WhAnew0,4009
7
- wagtail_newsletter_django_backend/models.py,sha256=XQvde_2C_TC0FkDpX2ILuBjoC0PZ9ydAJ9-yz6x0nMo,12917
7
+ wagtail_newsletter_django_backend/models.py,sha256=vo2IhXaTkJBkPN6ZWj0VER0fzh5IlRKeFzFLbRPUKxc,11757
8
8
  wagtail_newsletter_django_backend/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
9
9
  wagtail_newsletter_django_backend/urls.py,sha256=650q2IQKPOj6OSjVIB3QzxPl4n67M2MJwtFFEvJ3vOk,620
10
10
  wagtail_newsletter_django_backend/views.py,sha256=88UNdcJQzeTjLXJFI9chRgVHcowOZ_yvwe2v4_-FhVE,3200
@@ -15,6 +15,7 @@ wagtail_newsletter_django_backend/management/commands/send_scheduled_campaigns.p
15
15
  wagtail_newsletter_django_backend/migrations/0001_initial.py,sha256=DYuyU3MxhnhzKBPKlxZn2erP-lB9rGRUR4KngXRrBXw,11129
16
16
  wagtail_newsletter_django_backend/migrations/0002_alter_audience_name_alter_audiencesegment_name_and_more.py,sha256=-cvEvycnG5Q1bUyQOSyebfTDSrLND9TKTR7f2bEcbQU,1020
17
17
  wagtail_newsletter_django_backend/migrations/0003_alter_campaign_audience_segment.py,sha256=Z1A1VrcxqI7yHMcZgTU-Pv5SBBcAhIkMUeVQy-M9Hus,691
18
+ wagtail_newsletter_django_backend/migrations/0004_remove_audience_from_email_address_and_more.py,sha256=80N49u6TmFvZyfBJOronuP3l8CgVGrvlUat7TdCygm0,1364
18
19
  wagtail_newsletter_django_backend/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
20
  wagtail_newsletter_django_backend/templates/wagtail_newsletter_django_backend/manage.html,sha256=wrEOZgmu7YrE4J716e51FewJ-1WC4EXaJJyaIEbPucM,381
20
21
  wagtail_newsletter_django_backend/templates/wagtail_newsletter_django_backend/manage_tag.html,sha256=JfQIINFFA_aemgfzoOBY6LHdMgVBVoXOPbwiwsKRuDg,260
@@ -22,7 +23,7 @@ wagtail_newsletter_django_backend/templates/wagtail_newsletter_django_backend/un
22
23
  wagtail_newsletter_django_backend/templates/wagtail_newsletter_django_backend/unsubscribe_tag.html,sha256=DmM5O5ge-o7Q6y2rtiwOFyhzM9NaAtoq3AqCTi0cNvg,487
23
24
  wagtail_newsletter_django_backend/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
25
  wagtail_newsletter_django_backend/templatetags/wagtail_newsletter_django_backend_tags.py,sha256=-NF9xEhCjojtMgQFBJRWrelB13dYYZFtuiL-a3zSKx8,490
25
- wagtail_newsletter_django_backend-0.5.9.dist-info/licenses/LICENSE,sha256=mLNEGyMZ9q73ep-adKoMoTX06QDYEd949WxgTKhl93A,33970
26
- wagtail_newsletter_django_backend-0.5.9.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
27
- wagtail_newsletter_django_backend-0.5.9.dist-info/METADATA,sha256=ZQzxiqnYrcBv6z1kI1zVTVV11-W5xAYbXinUfg3OfpM,836
28
- wagtail_newsletter_django_backend-0.5.9.dist-info/RECORD,,
26
+ wagtail_newsletter_django_backend-0.6.0.dist-info/licenses/LICENSE,sha256=mLNEGyMZ9q73ep-adKoMoTX06QDYEd949WxgTKhl93A,33970
27
+ wagtail_newsletter_django_backend-0.6.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
28
+ wagtail_newsletter_django_backend-0.6.0.dist-info/METADATA,sha256=j245LHNYSX5anRd5ElKfMzRZ6kN1xNLyOZSkX3ckmPM,836
29
+ wagtail_newsletter_django_backend-0.6.0.dist-info/RECORD,,