Newsletter Builder is a free, open-source web application that makes school newsletters a team effort. Easily invite contributors, streamline article creation, and publish vibrant newsletters.
- Easy-to-use, minimalistic interface
- Articles can be scheduled in advance and assigned to contributors
- Automated reminder emails to keep contributors on track
- Consistent, professional presentation of text and photos
- Photo consent confirmation
- AI-powered feedback on article content and style
- Quality assurance process for editors to check articles before publishing
- Upcoming event calendar included at the bottom of every newsletter
- Recipient management
- One-click publication by email and to a public website
- Single sign-on (SSO) using Microsoft 365
-
Create a Postmark server.
-
Create an OpenAI account and generate an API key.
-
Create a general purpose v2 storage account in Microsoft Azure.
- Add private containers:
photos
,dataprotection
- Within the
dataprotection
blob container, upload a blank filekeys.xml
. Generate a SAS URL for this file with read/write permissions and a distant expiry. This will be used to store the application's data protection keys so that auth cookies persist across app restarts. - Add tables:
articles
,events
,newsletters
,recipients
,users
- The
users
table needs an initial entry, and this must be a valid user on your Azure tenant:- PartitionKey - the email domain of the user (everything after the
@
symbol) - RowKey - the email name of the user (everything before the
@
symbol) - IsEditor -
true
- FirstName - the user's first name
- DisplayName - the user's title and surname
- PartitionKey - the email domain of the user (everything after the
- Enable static website hosting, and set the index document to
index.html
and the error document to404.html
. This will create a$web
blob container, which will be the root of the public-facing newsletter website. - Customise the contents of the
StaticWebsite
folder in this repository, for example replacing placeholders with their appropriate values. Then upload to the$web
blob container. Also uploadlogo.jpg
(250x250px),logo-hd.jpg
(1200x1200px),favicon.ico
, andicon-192x192.png
. - Enable CORS for blob
GET
requests from the domain where the newsletter builder will be hosted (e.g.https://build.newsletter.example.com
).
- Add private containers:
-
Create an Azure CDN endpoint with the static website as its origin, and a custom domain set for where you would like your newsletter to be available online (e.g.
newsletter.example.com
). Enable compression. Add the following rules:- If request protocol =
HTTP
then URL redirect Found (302), HTTPS - If URL file extension =
jpg
,png
,pdf
,css
, orjs
(lowercase) then modify response header: overwriteCache-Control
max-age=31536000
, and then cache expiration: override365
days. - If URL file extension =
json
,html
, ortxt
(lowercase) then modify response header: overwriteCache-Control
no-cache
, and then cache expiration: bypass cache. - If URL path does not contain
.
then modify response header: overwriteCache-Control
no-cache
, and then cache expiration: bypass cache.
- If request protocol =
-
Create an Azure app registration.
- Name -
Newsletter Builder
- Redirect URI -
https://<your-newsletter-builder-domain>/signin-oidc
- Implicit grant - ID tokens
- Supported account types - Accounts in this organizational directory only
- API permissions -
Microsoft Graph - User.Read
- Token configuration - add optional claim of type ID:
upn
- Name -
-
Create an Azure App Service web app.
- Publish mode - Container
- Operating system - Linux
- Image source - Other container registries
- Docker Hub access type - Public
- Image and tag -
jamesgurung/newsletter-builder:latest
- Startup command: (blank)
-
Configure the following environment variables for the web app:
NewsletterEditorUrl
- the URL of the newsletter editorOrganisations__0__Name
- the name of your organisationOrganisations__0__Domain
- the domain of your user email addresses, e.g.example.com
Organisations__0__NewsletterUrl
- the URL of the published newsletterOrganisations__0__Address
- the address of your organisationOrganisations__0__Footer
- the footer text to include in emailsOrganisations__0__BannedWords
- a JSON array of words which are not allowed in articlesOrganisations__0__PhotoConsentUrl
- the URL of the photo consent spreadsheetOrganisations__0__UnlistedArticles__0
- an article name which should not be listed on the index page, for example a regular standing item (subsequent unlisted articles can be set up by adding additional items with incrementing indices)Organisations__0__FromEmail
- the email address from which newsletters and reminders will be sentOrganisations__0__QualityAssuranceEmail
- the email address to which quality assurance requests will be sentOrganisations__0__SocialMediaEmail
- the email address to receive requests to share the newsletter on social mediaOrganisations__0__ReminderReplyTo
- the reply-to address for remindersOrganisations__0__TwitterHandle
- the Twitter handle of your organisation, starting with an @ symbolOrganisations__0__DefaultDeadlineDaysBeforePublish
- the default number of days before the publish date that articles are dueOrganisations__0__AzureStorageStaticWebsiteAccountName
- the account name of the storage account where the newsletter static website is hostedOrganisations__0__AzureStorageStaticWebsiteAccountKey
- the account key of the storage account where the newsletter static website is hostedOrganisations__0__Reminders__0__DaysBeforeDeadline
- the number of days before the publish date to send the first reminder (subsequent reminders can be set up by adding additional items with incrementing indices)Organisations__0__Reminders__0__Subject
- the subject of the reminder emailOrganisations__0__Reminders__0__Message
- the message to include in the reminder emailAzure__ClientId
- the client ID of your Azure app registrationAzure__TenantId
- your Azure tenant IDAzure__StorageAccountName
- the name of your Azure Storage accountAzure__StorageAccountKey
- the key for your Azure Storage accountAzure__DataProtectionBlobUri
- the SAS URL for the keys file you created earlierOpenAI__Model
- the name of the OpenAI model to useOpenAI__Key
- your OpenAI API keyPostmarkServerToken
- the token for your Postmark serverAutomationApiKey
- a secret GUID which is used to authenticate requests to the automation API
-
Create scheduled tasks to call the
/api/automate/emailreminders/<your-email-domain>/<n>
endpoints for each reminder you configured, where<n>
is the index of the reminder.
If you have a question or feature request, please open an issue.
To contribute improvements to this project, or to adapt the code for the specific needs of your organisation, you are welcome to fork the repository.
Pull requests are welcome; please open an issue first to discuss.