{"id":26576697,"url":"https://github.com/worthwhile/django-herald","last_synced_at":"2025-04-06T05:15:17.550Z","repository":{"id":43147864,"uuid":"53439419","full_name":"worthwhile/django-herald","owner":"worthwhile","description":"A Django messaging library","archived":false,"fork":false,"pushed_at":"2024-02-13T17:38:54.000Z","size":249,"stargazers_count":227,"open_issues_count":14,"forks_count":26,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-30T04:08:21.778Z","etag":null,"topics":["attachment","disable-notifications","django","email","history","notifications","preview","sms"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/worthwhile.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-08T19:33:07.000Z","updated_at":"2025-03-07T02:38:27.000Z","dependencies_parsed_at":"2022-08-30T07:31:21.355Z","dependency_job_id":null,"html_url":"https://github.com/worthwhile/django-herald","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worthwhile%2Fdjango-herald","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worthwhile%2Fdjango-herald/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worthwhile%2Fdjango-herald/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worthwhile%2Fdjango-herald/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/worthwhile","download_url":"https://codeload.github.com/worthwhile/django-herald/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247436284,"owners_count":20938533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attachment","disable-notifications","django","email","history","notifications","preview","sms"],"created_at":"2025-03-23T03:26:12.221Z","updated_at":"2025-04-06T05:15:17.480Z","avatar_url":"https://github.com/worthwhile.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# django-herald\n\n[![Latest PyPI version](https://badge.fury.io/py/django-herald.svg)](https://pypi.python.org/pypi/django-herald)\n[![Tests](https://github.com/worthwhile/django-herald/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/worthwhile/django-herald/actions/workflows/ci.yml)\n[![Black](https://github.com/worthwhile/django-herald/actions/workflows/black.yml/badge.svg)](https://github.com/worthwhile/django-herald/actions/workflows/black.yml)\n[![Coverage Status](https://codecov.io/gh/worthwhile/django-herald/coverage.svg?branch=master)](https://app.codecov.io/gh/worthwhile/django-herald)\n\n[![Logo](https://github.com/worthwhile/django-herald/raw/master/logo.png)](https://github.com/worthwhile/django-herald)\n\nA Django messaging library that features:\n\n- Class-based declaration and registry approach, like Django Admin\n- Supports multiple transmission methods (Email, SMS, Slack, etc) per message\n- Browser-based previewing of messages\n- Maintains a history of messaging sending attempts and can view these messages\n- Disabling notifications per user\n\n# Python/Django Support\n\nWe try to make herald support all versions of django that django supports + all versions in between. \n\nFor python, herald supports all versions of python that the above versions of django support.\n\nSo as of herald v0.3 we support django 3.2 and 4.x+, and python 3.6, 3.7, 3.8, 3.9, and 3.10.\n\n# Installation\n\n1. `pip install django-herald`\n2. Add `herald` and `django.contrib.sites` to `INSTALLED_APPS`.\n3. Add herald's URLS:\n\n```python\nfrom django.conf import settings\nfrom django.conf.urls import url, include\n\nurlpatterns = []\n\nif settings.DEBUG:\n    urlpatterns = [\n        url(r'^herald/', include('herald.urls')),\n   ] + urlpatterns\n```\n\n# Usage\n\n1. Create a `notifications.py` file in any django app. This is where your notification classes will live. Add a class like this:\n\n```python\nfrom herald import registry\nfrom herald.base import EmailNotification\n\n\nclass WelcomeEmail(EmailNotification):  # extend from EmailNotification for emails\n    template_name = 'welcome_email'  # name of template, without extension\n    subject = 'Welcome'  # subject of email\n\n    def __init__(self, user):  # optionally customize the initialization\n        self.context = {'user': user}  # set context for the template rendering\n        self.to_emails = [user.email]  # set list of emails to send to\n\n    @staticmethod\n    def get_demo_args():  # define a static method to return list of args needed to initialize class for testing\n        from users.models import User\n        return [User.objects.order_by('?')[0]]\n\nregistry.register(WelcomeEmail)  # finally, register your notification class\n\n# Alternatively, a class decorator can be used to register the notification:\n\n@registry.register_decorator()\nclass WelcomeEmail(EmailNotification):\n    ...\n```\n\n\n2. Create templates for rendering the email using this file structure:\n\n        templates/\n            herald/\n                text/\n                    welcome_email.txt\n                html/\n                    welcome_email.html\n\n3. Test how your email looks by navigating to `/herald/`.\n\n4. Send your email wherever you need in your code:\n\n        WelcomeEmail(user).send()\n\n5. View the sent emails in django admin and even be able to resend it.\n\n## Email options\n\nThe following options can be set on the email notification class. For Example:\n\n```\nclass WelcomeEmail(EmailNotification):\n    cc = ['test@example.com']\n```\n\n- `from_email`: (`str`, default: `settings.DEFAULT_FROM_EMAIL`) email address of sender\n- `subject`: (`str`, default: ) email subject\n- `to_emails`: (`List[str]`, default: `None`) list of email strings to send to\n- `bcc`: (`List[str]`, default: `None`) list of email strings to send as bcc\n- `cc`: (`List[str]`, default: `None`) list of email strings to send as cc\n- `headers`: (`dict`, default: `None`) extra headers to be passed along to the `EmailMultiAlternatives` object\n- `reply_to`: (`List[str]`, default: `None`) list of email strings to send as the Reply-To emails\n- `attachments`: (`list`) list of attachments. See \"Email Attachments\" below for more info\n    \n    \n## Automatically Deleting Old Notifications\n\nHerald can automatically delete old notifications whenever a new notification is sent.\n\nTo enable this, set the `HERALD_NOTIFICATION_RETENTION_TIME` setting to a timedelta instance.\n\nFor example:\n\n```\nHERALD_NOTIFICATION_RETENTION_TIME = timedelta(weeks=8)\n```\n\nWill delete all notifications older than 8 weeks every time a new notification is sent.\n\n## Manually Deleting Old Notifications\n\nThe `delnotifs` command is useful for purging the notification history.\n\nThe default usage will delete everything from sent during today:\n\n```bash\npython manage.py delnotifs\n```\n\nHowever, you can also pass arguments for `start` or `end` dates. `end` is up to, but not including that date.\n- if only `end` is specified, delete anything sent before the end date.\n- if only `start` is specified, delete anything sent since the start date.\n- if both `start` and `end` are specified, delete anything sent in between, not including the end date.\n\n```bash\npython manage.py delnotifs --start='2016-01-01' --end='2016-01-10'\n```\n\n\n## Asynchronous Email Sending\n\nIf you are sending slightly different emails to a large number of people, it might take quite a while to process. By default, Django will process this all synchronously. For asynchronous support, we recommend django-celery-email. It is very straightfoward to setup and integrate: https://github.com/pmclanahan/django-celery-email\n\n\n## herald.contrib.auth\n\nDjango has built-in support for sending password reset emails. If you would like to send those emails using herald, you can use the notification class in herald.contrib.auth.\n\nFirst, add `herald.contrib.auth` to `INSTALLED_APPS` (in addition to `herald`).\n\nSecond, use the `HeraldPasswordResetForm` in place of django's built in `PasswordResetForm`. This step is entirely dependant on your project structure, but it essentially just involves changing the form class on the password reset view in some way:\n\n```python\n# you may simply just need to override the password reset url like so:\nurl(r'^password_reset/$', password_reset, name='password_reset', {'password_reset_form': HeraldPasswordResetForm}),\n\n# of if you are using something like django-authtools:\nurl(r'^password_reset/$', PasswordResetView.as_view(form_class=HeraldPasswordResetForm), name='password_reset'),\n\n# or you may have a customized version of the password reset view:\nclass MyPasswordResetView(FormView):\n    form_class = HeraldPasswordResetForm  # change the form class here\n\n# or, you may have a custom password reset form already. In that case, you will want to extend from the HeraldPasswordResetForm:\nclass MyPasswordResetForm(HeraldPasswordResetForm):\n    ...\n\n# alternatively, you could even just send the notification wherever you wish, seperate from the form:\nPasswordResetEmail(some_user).send()\n```\n\nThird, you may want to customize the templates for the email. By default, herald will use the `registration/password_reset_email.html` that is provided by django for both the html and text versions of the email. But you can simply override `herald/html/password_reset.html` and/or `herald/text/password_reset.txt` to suit your needs.\n\n## User Disabled Notifications\n\nIf you want to disable certain notifications per user, add a record to the UserNotification table and\nadd notifications to the disabled_notifications many to many table.\n\nFor example:\n\n```python\nuser = User.objects.get(id=user.id)\n\nnotification = Notification.objects.get(notification_class=MyNotification.get_class_path())\n\n# disable the notification\nuser.usernotification.disabled_notifications.add(notification)\n```\n\nBy default, notifications can be disabled.  You can put can_disable = False in your notification class and the system will\npopulate the database with this default.  Your Notification class can also override the verbose_name by setting it in your\ninherited Notification class.  Like this:\n\n```python\nclass MyNotification(EmailNotification):\n    can_disable = False\n    verbose_name = \"My Required Notification\"\n```\n\n## Email Attachments\n\nTo send attachments, assign a list of attachments to the attachments attribute of your EmailNotification instance, or override the get_attachments() method.\n\nEach attachment in the list can be one of the following:\n\n1. A tuple which consists of the filename, the raw attachment data, and the mimetype. It is up to you to get the attachment data. Like this:\n\n```python\nraw_data = get_pdf_data()\n\nemail.attachments = [\n    ('Report.pdf', raw_data, 'application/pdf'),\n    ('report.txt', 'text version of report', 'text/plain')\n]\nemail.send()\n```\n\n2. A MIMEBase object. See the documentation for attachments under EmailMessage Objects/attachments in the Django documentation.\n\n3. A django `File` object.\n\n### Inline Attachments\n\nSometimes you want to embed an image directly into the email content.  Do that by using a MIMEImage assigning a content id header to a MIMEImage, like this:\n\n```python\nemail = WelcomeEmail(user)\nim = get_thumbnail(image_file.name, '600x600', quality=95)\nmy_image = MIMEImage(im.read()) # MIMEImage inherits from MIMEBase\nmy_image.add_header('Content-ID', '\u003c{}\u003e'.format(image_file.name))\n```\n\nYou can refer to these images in your html email templates using the Content ID (cid) like this:\n\n```html\n\u003cimg src=\"cid:{{image_file.name}}\" /\u003e\n```\n\nYou would of course need to add the \"image_file\" to your template context in the example above.  You can also accomplish this using file operations.  In this example we overrode the get_attachments method of an EmailNotification.\n\n```python\nclass MyNotification(EmailNotification):\n    context = {'hello': 'world'}\n    template_name = 'welcome_email'\n    to_emails = ['somebody@example.com']\n    subject = \"My email test\"\n        \n    def get_attachments(self):\n        fp = open('python.jpeg', 'rb')\n        img = MIMEImage(fp.read())\n        img.add_header('Content-ID', '\u003c{}\u003e'.format('python.jpeg'))\n        return [\n            img,\n        ]\n```\n\nAnd in your template you would refer to it like this, and you would not need to add anything to the context:\n\n```html\n    \u003cimg src=\"cid:python.jpeg\" /\u003e\n```\n\n### HTML2Text Support\n\nDjango Herald can auto convert your HTML emails to plain text.  Any email without a plain text version\nwill be auto converted if you enable this feature.\n\n```\n# Install html2text\npip install django-herald[html2text]\n```\n\nIn your settings.py file:\n\n```\nHERALD_HTML2TEXT_ENABLED = True\n```\n\nYou can customize the output of HTML2Text by setting a configuration dictionary. See \n[HTML2Text Configuration](https://github.com/Alir3z4/html2text/blob/master/docs/usage.md) for options\n\n```\nHERALD_HTML2TEXT_CONFIG = {\n    # Key / value configuration of html2text \n    'ignore_images': True  # Ignores images in conversion\n}\n```\n\n```\nHERALD_RAISE_MISSING_TEMPLATES = True\n```\n\nBy default, Herald will raise an exception if a template is missing when true (default).\n\n### Twilio\n\n```\n# Install twilio\npip install django-herald[twilio]\n```\n\nYou can retrieve these values on [Twilio Console](https://twilio.com/console). Once you have retrieve the necessary ids, you can place those to your `settings.py`.\n\nFor reference, Twilio has some great tutorials for python.\n[Twilio Python Tutorial](https://www.twilio.com/docs/sms/quickstart/python)\n\n```\n# Twilio configurations\n# values taken from `twilio console`\nTWILIO_ACCOUNT_SID = \"your_account_sid\"\nTWILIO_AUTH_TOKEN = \"your_auth_token\"\nTWILIO_DEFAULT_FROM_NUMBER = \"+1234567890\"\n\n```\n\n### Other MIME attachments\n\nYou can also attach any MIMEBase objects as regular attachments, but you must add a content-disposition header, or they will be inaccessible:  \n\n```python\nmy_image.add_header('Content-Disposition', 'attachment; filename=\"python.jpg\"')\n```\n\nAttachments can cause your database to become quite large, so you should be sure to run the management commands to purge the database of old messages.\n\n# Running Tests\n\n```bash\npython runtests.py\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworthwhile%2Fdjango-herald","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fworthwhile%2Fdjango-herald","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworthwhile%2Fdjango-herald/lists"}