wbmailing 2.2.1__py2.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.

Potentially problematic release.


This version of wbmailing might be problematic. Click here for more details.

Files changed (57) hide show
  1. wbmailing/__init__.py +1 -0
  2. wbmailing/admin.py +74 -0
  3. wbmailing/apps.py +14 -0
  4. wbmailing/backend.py +131 -0
  5. wbmailing/celery.py +0 -0
  6. wbmailing/dynamic_preferences_registry.py +35 -0
  7. wbmailing/factories.py +211 -0
  8. wbmailing/filters/__init__.py +8 -0
  9. wbmailing/filters/mailing_lists.py +84 -0
  10. wbmailing/filters/mails.py +74 -0
  11. wbmailing/management/__init__.py +22 -0
  12. wbmailing/migrations/0001_initial_squashed_squashed_0008_alter_mail_bcc_email_alter_mail_cc_email_and_more.py +649 -0
  13. wbmailing/migrations/0002_delete_mailingsettings.py +16 -0
  14. wbmailing/migrations/0003_alter_mailinglistsubscriberchangerequest_options.py +25 -0
  15. wbmailing/migrations/__init__.py +0 -0
  16. wbmailing/models/__init__.py +6 -0
  17. wbmailing/models/mailing_lists.py +386 -0
  18. wbmailing/models/mails.py +895 -0
  19. wbmailing/serializers/__init__.py +19 -0
  20. wbmailing/serializers/mailing_lists.py +209 -0
  21. wbmailing/serializers/mails.py +251 -0
  22. wbmailing/tasks.py +37 -0
  23. wbmailing/templatetags/__init__.py +0 -0
  24. wbmailing/templatetags/mailing_tags.py +22 -0
  25. wbmailing/tests/__init__.py +0 -0
  26. wbmailing/tests/conftest.py +30 -0
  27. wbmailing/tests/models/__init__.py +0 -0
  28. wbmailing/tests/models/test_mailing_lists.py +297 -0
  29. wbmailing/tests/models/test_mails.py +205 -0
  30. wbmailing/tests/signals.py +124 -0
  31. wbmailing/tests/test_serializers.py +28 -0
  32. wbmailing/tests/test_tasks.py +49 -0
  33. wbmailing/tests/test_viewsets.py +216 -0
  34. wbmailing/tests/tests.py +142 -0
  35. wbmailing/urls.py +90 -0
  36. wbmailing/viewsets/__init__.py +32 -0
  37. wbmailing/viewsets/analytics.py +110 -0
  38. wbmailing/viewsets/buttons/__init__.py +10 -0
  39. wbmailing/viewsets/buttons/mailing_lists.py +91 -0
  40. wbmailing/viewsets/buttons/mails.py +98 -0
  41. wbmailing/viewsets/display/__init__.py +16 -0
  42. wbmailing/viewsets/display/mailing_lists.py +175 -0
  43. wbmailing/viewsets/display/mails.py +318 -0
  44. wbmailing/viewsets/endpoints/__init__.py +8 -0
  45. wbmailing/viewsets/endpoints/mailing_lists.py +86 -0
  46. wbmailing/viewsets/endpoints/mails.py +51 -0
  47. wbmailing/viewsets/mailing_lists.py +320 -0
  48. wbmailing/viewsets/mails.py +425 -0
  49. wbmailing/viewsets/menu/__init__.py +5 -0
  50. wbmailing/viewsets/menu/mailing_lists.py +37 -0
  51. wbmailing/viewsets/menu/mails.py +25 -0
  52. wbmailing/viewsets/titles/__init__.py +17 -0
  53. wbmailing/viewsets/titles/mailing_lists.py +63 -0
  54. wbmailing/viewsets/titles/mails.py +55 -0
  55. wbmailing-2.2.1.dist-info/METADATA +5 -0
  56. wbmailing-2.2.1.dist-info/RECORD +57 -0
  57. wbmailing-2.2.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,91 @@
1
+ from django.dispatch import receiver
2
+ from django.utils.translation import gettext as _
3
+ from rest_framework.reverse import reverse
4
+ from wbcore.contrib.directory.viewsets import (
5
+ CompanyModelViewSet,
6
+ EntryModelViewSet,
7
+ PersonModelViewSet,
8
+ )
9
+ from wbcore.contrib.icons import WBIcon
10
+ from wbcore.enums import RequestType
11
+ from wbcore.metadata.configs import buttons as bt
12
+ from wbcore.metadata.configs.buttons.view_config import ButtonViewConfig
13
+ from wbcore.signals.instance_buttons import add_instance_button
14
+
15
+
16
+ class MailingListSubcriptionRequestButtonConfig(ButtonViewConfig):
17
+ def get_custom_buttons(self):
18
+ if not hasattr(self.view.kwargs, "pk"):
19
+ user = self.request.user
20
+ if user.is_superuser or user.has_perm("wbmailing.administrate_mailinglistsubscriberchangerequest"):
21
+ return {
22
+ bt.ActionButton(
23
+ method=RequestType.GET,
24
+ identifiers=("wbmailing:mailinglistsubscriberchangerequest",),
25
+ endpoint=reverse(
26
+ "wbmailing:mailinglistsubscriberchangerequest-approveall", args=[], request=self.request
27
+ ),
28
+ label=_("Approve All"),
29
+ description_fields=_(
30
+ """
31
+ <p>Do you really want to approve all pending subscriptions requests?</p>
32
+ """
33
+ ),
34
+ action_label=_("Approving all pending subscription requests"),
35
+ title=_("Approve all pending subscription requests"),
36
+ )
37
+ }
38
+ return {}
39
+
40
+
41
+ class MailingListButtonConfig(ButtonViewConfig):
42
+ def get_custom_instance_buttons(self):
43
+ return {
44
+ bt.WidgetButton(key="mailevent_chart", label=_("Mail Penetration"), icon=WBIcon.FEEDBACK.icon),
45
+ }
46
+
47
+ def get_custom_list_instance_buttons(self):
48
+ return self.get_custom_instance_buttons()
49
+
50
+
51
+ class MailingListEmailContactThroughModelButtonConfig(ButtonViewConfig):
52
+ def get_custom_list_instance_buttons(self):
53
+ return {
54
+ bt.WidgetButton(key="requests", label="Changes Request History", icon=WBIcon.DATA_LIST.icon),
55
+ bt.ActionButton(
56
+ method=RequestType.PATCH,
57
+ identifiers=("directory:email_contacts",),
58
+ key="delete_from_mailinglist",
59
+ label=_("Delete"),
60
+ icon=WBIcon.DELETE.icon,
61
+ description_fields=_(
62
+ "<p> Are you sure you want to delete {{_entry.computed_str}} from this mailinglist? Check with your compliance team if you are not sure</p>"
63
+ ),
64
+ title=_("Delete"),
65
+ action_label=_("Deletion"),
66
+ ),
67
+ bt.ActionButton(
68
+ method=RequestType.PATCH,
69
+ identifiers=("directory:email_contacts",),
70
+ key="unsubscribe",
71
+ label=_("Unsubscribe"),
72
+ icon=WBIcon.UNDO.icon,
73
+ description_fields=_(
74
+ "<p> Are you sure you want to unsubscribe {{_entry.computed_str}} from this mailinglist? Until the entry exists, the user won't be able to resubscribe</p>"
75
+ ),
76
+ title=_("Unsubscribe"),
77
+ action_label=_("Unsubscribing"),
78
+ ),
79
+ }
80
+
81
+
82
+ @receiver(add_instance_button, sender=PersonModelViewSet)
83
+ @receiver(add_instance_button, sender=EntryModelViewSet)
84
+ @receiver(add_instance_button, sender=CompanyModelViewSet)
85
+ def crm_adding_instance_buttons_ep(sender, many, *args, **kwargs):
86
+ if not many:
87
+ return bt.DropDownButton(
88
+ label=_("Mailing"),
89
+ icon=WBIcon.UNFOLD.icon,
90
+ buttons=(bt.WidgetButton(key="mailinglist", label=_("Mailing"), icon=WBIcon.MAIL.icon),),
91
+ )
@@ -0,0 +1,98 @@
1
+ from django.utils.translation import gettext as _
2
+ from django.utils.translation import gettext_lazy
3
+ from rest_framework.reverse import reverse
4
+ from wbcore import serializers as wb_serializers
5
+ from wbcore.contrib.icons import WBIcon
6
+ from wbcore.enums import RequestType
7
+ from wbcore.metadata.configs import buttons as bt
8
+ from wbcore.metadata.configs.buttons.view_config import ButtonViewConfig
9
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
10
+ create_simple_display,
11
+ )
12
+ from wbmailing.models import MassMail
13
+
14
+
15
+ class MassMailButtonConfig(ButtonViewConfig):
16
+ def get_custom_instance_buttons(self):
17
+ class SendTestMailActionButtonSerializer(wb_serializers.Serializer):
18
+ to_test_email = wb_serializers.CharField(label=gettext_lazy("Send To"), default=self.request.user.email)
19
+
20
+ class Meta:
21
+ fields = ["to_test_email"]
22
+
23
+ return {
24
+ bt.ActionButton(
25
+ method=RequestType.PATCH,
26
+ identifiers=("wbmailing:entry-mailinglist",),
27
+ action_label=_("Sending test mail"),
28
+ key="send_test_mail",
29
+ description_fields=_("<p>Are you sure you want to send a test mail?</p>"),
30
+ serializer=SendTestMailActionButtonSerializer,
31
+ icon=WBIcon.CHART_SWITCHES.icon,
32
+ title=_("Test Mail"),
33
+ label=_("Test Mail"),
34
+ instance_display=create_simple_display([["to_test_email"]]),
35
+ ),
36
+ bt.WidgetButton(key="mails", label=_("Mails' Status"), icon=WBIcon.MAIL_OPEN.icon),
37
+ }
38
+
39
+
40
+ class MailButtonConfig(ButtonViewConfig):
41
+ def get_custom_instance_buttons(self):
42
+ title = gettext_lazy("Resend Mail")
43
+ action_label = gettext_lazy("Resending mail")
44
+ description = gettext_lazy("<p>Are you sure you want to resend the mail?</p>")
45
+ icon = WBIcon.REPLACE.icon
46
+ try:
47
+ mail = self.view.get_object()
48
+ if not mail.events.exists():
49
+ title = gettext_lazy("Send Mail")
50
+ action_label = gettext_lazy("Sending mail")
51
+ description = gettext_lazy("<p>Are you sure you want to send the mail?</p>")
52
+ icon = WBIcon.SEND.icon
53
+ except AssertionError:
54
+ pass
55
+ return {
56
+ bt.ActionButton(
57
+ method=RequestType.PATCH,
58
+ identifiers=("wbmailing:mail",),
59
+ action_label=action_label,
60
+ key="resend_mail",
61
+ description_fields=description,
62
+ icon=icon,
63
+ title=title,
64
+ label=title,
65
+ )
66
+ }
67
+
68
+ def get_custom_list_instance_buttons(self):
69
+ return self.get_custom_instance_buttons()
70
+
71
+
72
+ class MailStatusMassMailButtonConfig(ButtonViewConfig):
73
+ def get_custom_buttons(self):
74
+ if not self.view.kwargs.get("pk", None) and (mass_mail_id := self.view.kwargs.get("mass_mail_id", None)):
75
+ mass_mail = MassMail.objects.get(id=mass_mail_id)
76
+ if mass_mail.status == MassMail.Status.SENT:
77
+ return {
78
+ bt.ActionButton(
79
+ method=RequestType.PATCH,
80
+ identifiers=("wbmailing:massmail",),
81
+ endpoint=reverse(
82
+ "wbmailing:massmail-mailstatus-resendbouncedmails",
83
+ args=[mass_mail_id],
84
+ request=self.request,
85
+ ),
86
+ label=_("Resend Bounced Emails"),
87
+ description_fields=_(
88
+ """
89
+ <p>Do not abuse this function! Doing so will likely degrade your mailing reputation.</p>
90
+ <p>If you are sure, please confirm</>
91
+ <p>
92
+ """
93
+ ),
94
+ action_label=_("Resending bounced emails"),
95
+ title=_("Resend Bounced Emails"),
96
+ )
97
+ }
98
+ return set()
@@ -0,0 +1,16 @@
1
+ from .mailing_lists import (
2
+ EmailContactMailingListDisplayConfig,
3
+ MailingListDisplayConfig,
4
+ MailingListEntryCompanyDisplayConfig,
5
+ MailingListEntryDisplayConfig,
6
+ MailingListSubscriberChangeRequestDisplayConfig,
7
+ MailingListSubscriberRequestEntryDisplayConfig,
8
+ MailingListSubscriberRequestMailingListDisplayConfig,
9
+ )
10
+ from .mails import (
11
+ MailDisplayConfig,
12
+ MailEventDisplayConfig,
13
+ MailStatusMassMailDisplayConfig,
14
+ MailTemplateDisplayConfig,
15
+ MassMailDisplayConfig,
16
+ )
@@ -0,0 +1,175 @@
1
+ from typing import Optional
2
+
3
+ from django.utils.translation import gettext as _
4
+ from wbcore.contrib.color.enums import WBColor
5
+ from wbcore.metadata.configs import display as dp
6
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
7
+ Display,
8
+ create_simple_display,
9
+ create_simple_section,
10
+ )
11
+ from wbcore.metadata.configs.display.instance_display.utils import repeat_field
12
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
13
+ from wbmailing import models
14
+
15
+
16
+ class MailingListDisplayConfig(DisplayViewConfig):
17
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
18
+ return dp.ListDisplay(
19
+ fields=[
20
+ dp.Field(key="title", label=_("Title")),
21
+ dp.Field(key="nb_subscribers", label=_("Subscribers")),
22
+ dp.Field(key="is_public", label=_("Public")),
23
+ ]
24
+ )
25
+
26
+ def get_instance_display(self) -> Display:
27
+ return create_simple_display(
28
+ [["title", "nb_subscribers", "is_public"], [repeat_field(3, "email_contacts_section")]],
29
+ [
30
+ create_simple_section(
31
+ "email_contacts_section",
32
+ _("Email Contacts"),
33
+ [["email_contacts"]],
34
+ "email_contacts",
35
+ collapsed=True,
36
+ )
37
+ ],
38
+ )
39
+
40
+
41
+ class MailingListSubscriberChangeRequestDisplayConfig(DisplayViewConfig):
42
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
43
+ return dp.ListDisplay(
44
+ fields=[
45
+ dp.Field(key="type", label=_("Type")),
46
+ dp.Field(key="email_contact", label=_("Email Contact")),
47
+ dp.Field(key="entry_repr", label=_("Entry")),
48
+ dp.Field(key="requester", label=_("Requester")),
49
+ dp.Field(key="mailing_list", label=_("Mailing List")),
50
+ dp.Field(key="created", label=_("Creation Date")),
51
+ dp.Field(key="expiration_date", label=_("Expiration Date")),
52
+ dp.Field(key="reason", label=_("Reason")),
53
+ ],
54
+ legends=[
55
+ dp.Legend(
56
+ key="status",
57
+ items=[
58
+ dp.LegendItem(
59
+ icon=WBColor.YELLOW_LIGHT.value,
60
+ label=models.MailingListSubscriberChangeRequest.Status.PENDING.label,
61
+ value=models.MailingListSubscriberChangeRequest.Status.PENDING,
62
+ ),
63
+ dp.LegendItem(
64
+ icon=WBColor.GREEN_LIGHT.value,
65
+ label=models.MailingListSubscriberChangeRequest.Status.APPROVED.label,
66
+ value=models.MailingListSubscriberChangeRequest.Status.APPROVED,
67
+ ),
68
+ dp.LegendItem(
69
+ icon=WBColor.RED_LIGHT.value,
70
+ label=models.MailingListSubscriberChangeRequest.Status.DENIED.label,
71
+ value=models.MailingListSubscriberChangeRequest.Status.DENIED,
72
+ ),
73
+ ],
74
+ )
75
+ ],
76
+ formatting=[
77
+ dp.Formatting(
78
+ column="status",
79
+ formatting_rules=[
80
+ dp.FormattingRule(
81
+ style={"backgroundColor": WBColor.YELLOW_LIGHT.value},
82
+ condition=(
83
+ "==",
84
+ models.MailingListSubscriberChangeRequest.Status.PENDING,
85
+ ),
86
+ ),
87
+ dp.FormattingRule(
88
+ style={"backgroundColor": WBColor.GREEN_LIGHT.value},
89
+ condition=(
90
+ "==",
91
+ models.MailingListSubscriberChangeRequest.Status.APPROVED,
92
+ ),
93
+ ),
94
+ dp.FormattingRule(
95
+ style={"backgroundColor": WBColor.RED_LIGHT.value},
96
+ condition=(
97
+ "==",
98
+ models.MailingListSubscriberChangeRequest.Status.DENIED,
99
+ ),
100
+ ),
101
+ ],
102
+ )
103
+ ],
104
+ )
105
+
106
+ def get_instance_display(self) -> Display:
107
+ return create_simple_display(
108
+ [
109
+ [repeat_field(3, "status")],
110
+ ["email_contact", "mailing_list", "expiration_date"],
111
+ [
112
+ "requester",
113
+ "approver",
114
+ ".",
115
+ ],
116
+ [
117
+ "created",
118
+ "updated",
119
+ ".",
120
+ ],
121
+ [repeat_field(3, "reason")],
122
+ ]
123
+ )
124
+
125
+
126
+ class MailingListSubscriberRequestMailingListDisplayConfig(MailingListSubscriberChangeRequestDisplayConfig):
127
+ def get_instance_display(self) -> Display:
128
+ return create_simple_display(
129
+ [
130
+ [repeat_field(2, "status")],
131
+ ["expiration_date", "email_contact"],
132
+ ]
133
+ )
134
+
135
+
136
+ class EmailContactMailingListDisplayConfig(DisplayViewConfig):
137
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
138
+ return dp.ListDisplay(
139
+ fields=[
140
+ dp.Field(key="status", label=_("Status")),
141
+ dp.Field(key="email_contact", label=_("Email Contact")),
142
+ dp.Field(key="expiration_date", label=_("Expiration Date")),
143
+ dp.Field(key="is_pending_request_change", label=_("Pending Change")),
144
+ ]
145
+ )
146
+
147
+
148
+ class MailingListEntryDisplayConfig(MailingListDisplayConfig):
149
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
150
+ return dp.ListDisplay(
151
+ fields=[
152
+ dp.Field(key="status", label=_("Status")),
153
+ dp.Field(key="mailing_list", label=_("Mailing List")),
154
+ dp.Field(key="is_public", label=_("Public")),
155
+ dp.Field(key="expiration_date", label=_("Expiration Date")),
156
+ dp.Field(key="is_pending_request_change", label=_("Pending Change")),
157
+ ]
158
+ )
159
+
160
+
161
+ class MailingListEntryCompanyDisplayConfig(MailingListEntryDisplayConfig):
162
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
163
+ return dp.ListDisplay(
164
+ fields=[
165
+ dp.Field(key="title", label=_("Mailing List")),
166
+ dp.Field(key="is_public", label=_("Public")),
167
+ ]
168
+ )
169
+
170
+
171
+ class MailingListSubscriberRequestEntryDisplayConfig(MailingListSubscriberChangeRequestDisplayConfig):
172
+ def get_instance_display(self) -> Display:
173
+ return create_simple_display(
174
+ [[repeat_field(3, "status")], ["email_contact", "mailing_list", "expiration_date"]]
175
+ )