Package: django-simple-bulk-emailer
The django-simple-bulk-emailer Django app creates and distributes customizable bulk emails suitable for news articles and similar uses.
Optionally, it can produce customizable drop-in website article pages along with index pages for each subscription type.
Article pages include social media Open Graph tags, with the ability to specify a default image for pages without an image assigned.
The app can produce an unlimited number of subscriptions (email distribution lists), with different email and page templates for each.
Customizable user subscription management pages and emails are built in.Refer to theDjango email documentationfor information on configuring a website to send email.Quick startRunpip installdjango-simple-bulk-emailer.Add django-simple-bulk-emailer and its dependencies to your installed apps:INSTALLED_APPS = (
    ...
    'django.contrib.sites',

    'adminsortable2',
    'ckeditor',
    'django_simple_bulk_emailer',
    'django_simple_file_handler',
)Runpython manage.py migrate.Include the django-simple-bulk-emailer URLconf in your website’surls.pylike this:urlpatterns = [
    ...
    url(
        r'^article-pages-path/',
        include('django_simple_bulk_emailer.urls')
    ),
]5. If you are already using the Django sites framework, runpython manage.py import_sites.
You will then need to go to BULK EMAIL > Site profiles in your admin site and check to be sure your details are correct.
All new site profiles will be assigned anhttps://protocol, so change this if necessary.If you are not already using the Django sites framework, go to your admin site and, under BULK EMAIL > Site profiles, create a profile with your site’s information.
TheSITE_IDsetting is needed for django-simple-bulk-emailer.
See theDjango sites framework documentationfor more information.6. If you do not have Django sessions enabled (they are enabled by default), they will need to be enabled.
See theDjango sessions documentationfor more information.BasicsSubscriptions (email distribution lists) are created and managed via the admin site.
Monthly email opens can also be tracked in the admin site. See the section on management commands for more information.For information on customizable settings for images and linked files used with emails and pages, see thedjango-simple-file-handler documentation.SubscriptionsSubscriptions (email distribution lists) are configured in the admin site.
The order of subscriptions can be changed by drag-and-drop, and this same order will be used in the options for choosing a distribution list when creating a bulk email.The change list for this section of the admin site includes links to public-facing article index pages for subscriptions that use them.
Index page URLs follow the formatexample.com/article-pages-path/subscription-name-in-slug-form/.Primary fieldsThese apply to all subscriptions.List name— What you want the list to be called in user-facing pages.Descriptive text— Optional text that will appear at the top of the subscription’s article index page.Publicly visible— A checkbox that determines whether the subscription is made available in user subscription-management pages. You may want private lists for testing purposes, etc. This also must be checked for page views to work.Use page view— A checkbox that determines whether article and index website pages will be generated for emails in the list.MailChimp syncThese fields are self-explanatory and only need to be configured if you want to sync subscribers to this subscription with a MailChimp audience’s contacts.
More information on MailChimp syncing is available below.Advanced settingsThese fields only need to be configured if the subscription needs to use custom templates or a custom email model (for example, if you want to subclass the BulkEmail model multiple times and have separate subscriptions associated with each).
Proceed with caution, as page views and email sending will not work correctly if these are not configured properly.Email template directory— Defaults to ‘django_simple_bulk_emailer/subscription/emails’. See the section on custom templates for more information.Page template directory— Defaults to ‘django_simple_bulk_emailer/subscription/pages’. See the section on custom templates for more information.Associated model— Defaults to ‘django_simple_bulk_emailer.models.BulkEmail’. If set, assigns a different email model for use with a subscription so views and admin site sections will continue to work. If the subscription is only being used to manage MailChimp subscribers through your website, set this to ‘None.’ This will keep it from appearing as an option for the app’s email distribution.SubscribersNames and email addresses for subscribers, as well as their subscriptions, can be changed through the admin site in addition to the user-facing pages.The user-facing subscription page is atexample.com/article-pages-path/subscriptions/subscribe.Bulk emailsThe change list for this section of the admin site includes links to preview the HTML version of each email, as well as its article page (if pages are being used).
Emails are sent from the email preview pages.Links to user-facing article pages can be found on the subscription index pages. See the section on subscriptions for more information.
URLs for user-facing article pages follow the formatexample.com/article-pages-path/subscription-name-in-slug-form/2019/8/1/19/email-headline-in-slug-form.html.
If you remove “page-preview/” from the URL of the page preview, you will have the URL of the public-facing page.
with the first three numbers representing the year, month and day of publication and the fourth representing the database object’s ID.
URL patterns only use the ID to retrieve the article, so feel free to change the headline or publication date after publication without fear of creating broken links.Primary fieldsSubscription list— Which distribution list the email should be sent to. This also determines which index page the article will appear on.Headline— The email/article’s headline, which will also appear in the email’s subject line.Body text— This is a django-ckeditor field that allows you to create the HTML body text for your email with a WYSIWYG editor. See thedjango-ckeditor documentationfor information on customizing the editor. A sample configuration is given below.Published— A checkbox determining whether the public article page is accessible and whether the article appears on the subscription’s index page.Has been updated— A checkbox to signify that changes have been made to the email. Adds “Updated: “ to the beginning of the email subject line.Publication date— Defaults to the date the email was created.Deletion date— An optional date the email/article should be deleted from the database. See the sections on optional settings and management commands for more information.ImageThese fields only need to be configured if the email should have an image associated with it.Image size— Select from preset options how large the image should be. See the section on optional settings for more information.Screen reader description— Alt text to be associated with the image.Image caption— An optional caption to be displayed with the image.Uploaded file— The image file to be imported for processing.DocumentsOptionally, documents can be linked from the email/article page. Once the email has been saved, the documents can be arranged by drag-and-drop. The email must be saved again to preserve the order.Title— The name of the file to be displayed as a link. This also will be used to create a new file name for the file once it is uploaded.Extra text— Optional text to be displayed next to the file name link.Uploaded file— The document file to be imported for processing.Integrating article page templatesTo integrate article pages into your website, add the following to yourbase.htmltemplate:{% block emailer_head%}{%endblock %}between the template’s<head></head>tags.{% block content%}{%endblock %}between the template’s<body></body>tags.{% block emailer_foot %}between the template’s<body></body>tags and below{% block content %}. This is used to load a JavaScript file for the pages’ responsive design.Custom templatesCopying templates from the app and modifying them is the easiest way to create custom templates.TheBulkEmailmodel is the basis of both bulk emails and article pages.
It includes the following fields that may be useful in creating your own templates or accessing instances with your own code:headline—CharField,max_lengthof 255secondary_headline—TextFieldupdate_text—TextFieldbody_text—RichTextField(django-ckeditor HTML)publication_date—DateFielddeletion_date—DateFieldpublished—BooleanFieldUsefulBulkEmailmodel methods include:short_headline— Returns a string of 30 characters or fewer, ending in an ellipsis if necessaryfirst_paragraph— Returns the first paragraph from thebody_textfield, minus HTML tags, as a stringemail_headline— returns theheadlinefield, but is easily overridden if subclassing the modelemail_body— Returns thebody_textfield, after making any substitutions specified in theEMAILER_SUBSTITUTIONSsetting, as a stringemail_image— Returns theEmailImageinstance associated with theBulkEmailinstance if one existsemail_documents— Returns a set of allEmailDocumentinstances associated with theBulkEmailinstancesubscription_name— Returns the name of the subscription associated with theBulkEmailinstance as a stringsubscription_url— Returns the URL for the index page for the subscription associated with theBulkEmailinstance as a stringpage_url— Returns the URL of the article page associated with theBulkEmailinstance as a stringprotocol_domain— Returns the domain of the website, along with its protocol, as a stringstatic_domain— Returns the domain of the website, along with its protocol,  as a string, unless the site’sSTATIC_URLsetting specifies a protocol and domainmedia_domain— Returns the domain of the website, along with its protocol,  as a string, unless the site’sMEDIA_URLsetting specifies a protocol and domainUsefulEmailImagefields include:description—CharField,max_lengthof 255caption—TextFieldimage_width—PositiveIntegerFieldprocessed_file—FileFieldUsefulEmailImagemethods include:image_url— Returns the relative URL of the image in theprocessed_filefield as a stringimage_height— Returns an integer for the height of the image in theprocessed_filefieldimage_width— Returns an integer for the width of the image in theprocessed_filefieldUsefulEmailDocumentfields include:title—CharField,max_lengthof 245extra_text—TextFieldsaved_file—FileFieldsort_order—PositiveIntegerFieldUsefulEmailDocumentmethods include:file_url— Returns the relative URL of the file in thesaved_filefield as a stringEmail templatesTemplate names includeemail_template_html.htmlandemail_template_text.txt.Custom HTML email templates must begin with the{% extends basic_template %}tag.
This allows the template to be loaded into both the email preview page and the basic template used for email sending.TheBulkEmailinstance is accessible in the template as{{ email_instance }}.
See the information on fields and methods above.Article page templatesTemplate names includelist_view.htmlandpage_view.html.TheBulkEmailinstance is accessible in the page template as{{ email_instance }}.
See the information on fields and methods above.Other templatesFor information on customizing other templates, see the section on optional settings.Form securityIfdjango-recaptchais installed and configured, django-simple-bulk-emailer will use it with the subscription form.
See the section on optional settings for more information.Built-in security includes use of a hidden “honeypot” field and monitoring of the form’s time-to-submit.
If either of these measures is violated, the form will appear to submit, but no data will be processed.MailChimpMailChimp syncing is optional. if you wish to sync with MailChimp, first runpip install mailchimp3.Changes made through MailChimp are synced immediately through a webhook. See the configuration information below.
Changes made locally are synced to MailChimp when thesync_mailchimpmanagement command is run.Other MailChimp notes:If a user changes their email address through MailChimp to one that already exists locally, the two local subscribers will be merged.Deleting a subscriber locally unsubscribes the contact in MailChimp, since deleting the address there would not allow it to be subscribed again in the future.If an email address is banned from an audience list by MailChimp, the subscription will be removed from the subscriber locally.Once an email address is unsubscribed from a MailChimp audience list, further changes on either end will not be synced due to limitations of the MailChimp API. As a result, a new contact will be created in the MailChimp audience list if the subscriber’s email address has been changed locally while unsubscribed and the user is then resubscribed.Configuring a Mailchimp webhookThe URL to use with the webhook will follow the formathttps://www.example.com/article-pages-path/mc-sync/sync?key=secretkeywheresecretkeyis the secret key listed in your subscription’s MailChimp sync settings. Because MailChimp does not support other forms of authentication for webhooks, this key is used and changes after each request. MailChimp’s webhook URL is then updated automatically.Check the boxes for what to sync:SubscribesUnsubscribesProfile updatesCleaned addressEmail changedAlso check the boxes for circumstances to sync:By a subscriberBy an account adminImportant: Do not check the box for “From API,” or you will create a syncing loop between the two systems.Optional settingsEMAILER_EMAIL_TEMPLATES— A string representing the path to a directory of email templates used by all subscriptions. Defaults to ‘django_simple_bulk_emailer/universal/emails’.EMAILER_PAGE_TEMPLATES— A string representing the path to a directory of page templates used by all subscriptions. Defaults to ‘django_simple_bulk_emailer/universal/pages’.EMAILER_FROM_ADDRESS— String. If set, this is the “from” address to be used in emails. Defaults to theDEFAULT_FROM_EMAILsetting.EMAILER_REPLY_ADDRESS— String. If set, this is the “reply-to” address to be used in emails. Defaults to the “from” address.EMAILER_SUBSTITUTIONS— Dictionary. If set, determines which substitutions will be made in the HTML of the emails’ body text. A sample dictionary is available below.EMAILER_EMAIL_DELETE_DAYS— Positive integer. If set, gives the deletion date field a default value this many days from the current date. Deletion date is only used if the management command is executed. Does not affect tracking of email opens.EMAILER_TRACKING_MONTHS— Positive integer. Number of months for emails to be tracked for monthly stats. Defaults to 3. The higher this number is set, the longer theupdate_email_statsmanagement command will take to run. Once removed from tracking, emails cannot be reinstated.EMAILER_STATS_SAVED— Positive integer. Number of most-recent monthly stats to keep in the database if thedelete_expired_statsmanagement command is run. Defaults to 12.EMAILER_PAGINATION— Boolean. If set to False, will stop list views from being paginated. Defaults to True.EMAILER_PAGINATION_RESULTS— Positive integer. If set, determines the number of results per page in list view. Defaults to 10.EMAILER_IMAGE_WIDTHS— A list of tuples. If set, will change the image width choices in the admin. Images will be scaled proportionally. The default widths list is given as an example below.EMAILER_SUBSCRIBE_SUBJECT— A string used as the subject line for an email sent to someone entering an email address in the subscription page. Defaults to ‘Manage your email subscriptions’.EMAILER_RECAPTCHA_TYPE— Integer. Selects which version of reCAPTCHA to use if django-recaptcha is installed and configured. Choices are 1 (v2 checkbox), 2 (v2 invisible) or 3 (v3). Defaults to 1.EMAILER_RECAPTCHA_ATTRS— Dictionary. Data attributes to be passed on to the reCAPTCHA field. See django-recaptcha documentation for more information.EMAILER_RECAPTCHA_PARAMS— Dictionary. API parameters to be passed on to the reCAPTCHA field. See django-recaptcha documentation for more information.EMAILER_DEFAULT_IMAGE— String. Full URL for a default image (such as a logo) to be used when sharing email pages to social media when no image is included in the email.EMAILER_DEFAULT_TYPE— String. Image type for default image, such as'image/png'.EMAILER_DEFAULT_WIDTH— String. Width for default image.EMAILER_DEFAULT_HEIGHT— String. Height for default image.EMAILER_DEFAULT_ALT— String. Alt text for default image.Management commandsIt is recommended that these commands be run by cron jobs or another method on a regular schedule. It is also recommended that the text output be written to a log file.send_bulk_email— Goes through subscriptions in the order they are ranked in the admin and sends whichever bulk email was marked for sending first. This is to limit how long the function takes to execute and make it friendlier to “serverless” deployments such as AWS Lambda. Because it only sends one email, you may need to run this frequently.sync_mailchimp— If MailChimp is configured, syncs local subscriber changes to MailChimp.delete_unsubscribed_users— Optional command that removes subscribers who do not have any subscriptions and were created at least one day ago.delete_expired_emails— Optional command that deletes emails which have reached or passed their deletion date.delete_expired_stats— Optional command that deletes monthly stats which have reached or passed their deletion date.update_email_stats— Optional command that updates the monthly statistics for email opens. It is suggested that this be run daily.Advanced useThe app uses modular, reusable mixins and functions that can, of course, be imported for use with your own code.You may wish to create a custom bulk email app by subclassing elements of this app.
For instance, you may wish to override theemail_subject,email_headlineandemail_bodymethods on theBulkEmailmodel.
It is suggested you use proxy models to get any of this app’s models you do not need to customize into your app.
Note that classes in the app’sadmin.pyfile makes use oftop_fieldsetsandbottom_fieldsets(along withmiddle_fieldsetsinBulkEmailAdmin) to allow you to subclass these and insert your own fields at various points in the admin change page.Sample settingsSample substitution dictionaryEMAILER_SUBSTITUTIONS = {
    ' target="_blank"': '',
}Default widths listEMAILER_IMAGE_WIDTHS = [
    (1200, 'Banner'),
    (900, 'Large'),
    (600, 'Medium'),
    (300, 'Small'),
]Sample django-ckeditor configuration settingsCKEDITOR_CONFIGS = {
    'default': {
        'disableNativeSpellChecker': False,
        'resize_dir': 'both',
        'width': '100%',
        'toolbar': 'Custom',
        'toolbar_Custom': [
            ['Undo', 'Redo'],
            ['Find', 'Replace'],
            ['Source'],
            ['Maximize'],
            '/',
            ['Format', 'Bold', 'Italic', 'Underline', 'Strike'],
            ['RemoveFormat'],
            ['NumberedList','BulletedList', '-', 'Outdent', 'Indent'],
            ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
            ['Subscript', 'Superscript'],
            ['SpecialChar', 'PasteText',],
            ['Link', 'Unlink'],
        ],
        'allowedContent': True,
        'extraAllowedContent': 'iframe[*]',
    },
}

TEXT_ADDITIONAL_TAGS = [
    'iframe',
]

TEXT_ADDITIONAL_ATTRIBUTES = [
    'scrolling',
    'allowfullscreen',
    'webkitallowfullscreen',
    'mozallowfullscreen',
    'frameborder',
    'src',
    'height',
    'width',
]