1
0
mirror of https://github.com/bitwarden/server synced 2025-12-06 00:03:34 +00:00

chore(docs): Updated docs for IMailer and MJML

* Updated docs for IMailer.

* More changes.

* Added deprecation context.

* ViewModel corrections.

* Updated link.

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* Updated link.

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* Updated steps for clarity.

* Update src/Core/MailTemplates/Mjml/README.md

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* Grammar fix.

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
This commit is contained in:
Todd Martin
2025-11-24 23:48:53 -05:00
committed by GitHub
parent 5fb69e42b0
commit ebe5306fd2
3 changed files with 67 additions and 51 deletions

View File

@@ -1,16 +1,15 @@
# MJML email templating
# `MJML` email templating
This directory contains MJML templates for emails. MJML is a markup language designed to reduce the pain of coding responsive email templates. Component based development features in MJML improve code quality and reusability.
This directory contains `MJML` templates for emails. `MJML` is a markup language designed to reduce the pain of coding responsive email templates. Component-based development features in `MJML` improve code quality and reusability.
MJML stands for MailJet Markup Language.
> [!TIP]
> `MJML` stands for MailJet Markup Language.
## Implementation considerations
These `MJML` templates are compiled into HTML which will then be further consumed by our Handlebars mail service. We can continue to use this service to assign values from our View Models. This leverages the existing infrastructure. It also means we can continue to use the double brace (`{{}}`) syntax within MJML since Handlebars can be used to assign values to those `{{variables}}`.
`MJML` templates are compiled into `HTML`, and those outputs are then consumed by Handlebars to render the final email for delivery. It builds on top of our existing infrastructure and means we can continue to use the double brace (`{{}}`) syntax within `MJML`, since Handlebars will assign values to those `{{variables}}`.
There is no change on how we interact with our view models.
There is an added step where we compile `*.mjml` to `*.html.hbs`. `*.html.hbs` is the format we use so the handlebars service can apply the variables. This build pipeline process is in progress and may need to be manually done at times.
To do this, there is an added step where we compile `*.mjml` to `*.html.hbs`. `*.html.hbs` is the format we use so the Handlebars service can apply the variables. This build pipeline process is in progress and may need to be manually done at times.
### `*.txt.hbs`
@@ -37,45 +36,50 @@ npm run build:minify
npm run prettier
```
## Development
## Development process
MJML supports components and you can create your own components by adding them to `.mjmlconfig`. Components are simple JavaScript that return MJML markup based on the attributes assigned, see components/mj-bw-hero.js. The markup is not a proper object, but contained in a string.
`MJML` supports components and you can create your own components by adding them to `.mjmlconfig`. Components are simple JavaScript that return `MJML` markup based on the attributes assigned, see components/mj-bw-hero.js. The markup is not a proper object, but contained in a string.
When using MJML templating you can use the above [commands](#building-mjml-files) to compile the template and view it in a web browser.
When using `MJML` templating you can use the above [commands](#building-mjml-files) to compile the template and view it in a web browser.
Not all MJML tags have the same attributes, it is highly recommended to review the documentation on the official MJML website to understand the usages of each of the tags.
Not all `MJML` tags have the same attributes, it is highly recommended to review the documentation on the official MJML website to understand the usages of each of the tags.
### Recommended development - IMailService
### Developing the mail template
#### Mjml email template development
1. Create `cool-email.mjml` in appropriate team directory.
2. Run `npm run build:watch`.
3. View compiled `HTML` output in a web browser.
4. Iterate through your development. While running `build:watch` you should be able to refresh the browser page after the `mjml/js` recompile to see the changes.
1. create `cool-email.mjml` in appropriate team directory
2. run `npm run build:watch`
3. view compiled `HTML` output in a web browser
4. iterate -> while `build:watch`'ing you should be able to refresh the browser page after the mjml/js re-compile to see the changes
### Testing the mail template with `IMailer`
#### Testing with `IMailService`
After the email is developed in the [initial step](#developing-the-mail-template), we need to make sure that the email `{{variables}}` are populated properly by Handlebars. We can do this by running it through an `IMailer` implementation. The `IMailer`, documented [here](../../Platform/Mail/README.md#step-3-create-handlebars-templates), requires that the ViewModel, the `.html.hbs` `MJML` build artifact, and `.text.hbs` files be in the same directory.
After the email is developed from the [initial step](#mjml-email-template-development) make sure the email `{{variables}}` are populated properly by running it through an `IMailService` implementation.
1. Run `npm run build:hbs`.
2. Copy built `*.html.hbs` files from the build directory to the directory that the `IMailer` expects. All files in the `Core/MailTemplates/Mjml/out` directory should be copied to the `/src/Core/MailTemplates/Mjml` directory, ensuring that the files are in the same directory as the corresponding ViewModels. If a shared component is modified it is important to copy and overwrite all files in that directory to capture changes in the `*.html.hbs` files.
3. Run code that will send the email.
1. run `npm run build:hbs`
2. copy built `*.html.hbs` files from the build directory to a location the mail service can consume them
1. all files in the `Core/MailTemplates/Mjml/out` directory can be copied to the `src/Core/MailTemplates/Handlebars/MJML` directory. If a shared component is modified it is important to copy and overwrite all files in that directory to capture
changes in the `*.html.hbs`.
3. run code that will send the email
The minified `html.hbs` artifacts are deliverables and must be placed into the correct `/src/Core/MailTemplates/Mjml` directories in order to be used by `IMailer` implementations, see step 2 above.
### Testing the mail template with `IMailService`
> [!WARNING]
> The `IMailService` has been deprecated. The [IMailer](#testing-the-mail-template-with-imailer) should be used instead.
After the email is developed from the [initial step](#developing-the-mail-template), make sure the email `{{variables}}` are populated properly by running it through an `IMailService` implementation.
1. Run `npm run build:hbs`
2. Copy built `*.html.hbs` files from the build directory to a location the mail service can consume them.
1. All files in the `Core/MailTemplates/Mjml/out` directory should be copied to the `src/Core/MailTemplates/Handlebars/MJML` directory. If a shared component is modified it is important to copy and overwrite all files in that directory to capture changes in the `*.html.hbs`.
3. Run code that will send the email.
The minified `html.hbs` artifacts are deliverables and must be placed into the correct `src/Core/MailTemplates/Handlebars/` directories in order to be used by `IMailService` implementations, see 2.1 above.
### Recommended development - IMailer
TBD - PM-26475
### Custom tags
There is currently a `mj-bw-hero` tag you can use within your `*.mjml` templates. This is a good example of how to create a component that takes in attribute values allowing us to be more DRY in our development of emails. Since the attribute's input is a string we are able to define whatever we need into the component, in this case `mj-bw-hero`.
In order to view the custom component you have written you will need to include it in the `.mjmlconfig` and reference it in an `mjml` template file.
In order to view the custom component you have written you will need to include it in the `.mjmlconfig` and reference it in a `.mjml` template file.
```html
<!-- Custom component implementation-->
<mj-bw-hero
@@ -84,8 +88,7 @@ In order to view the custom component you have written you will need to include
/>
```
Attributes in Custom Components are defined by the developer. They can be required or optional depending on implementation. See the official MJML documentation for more information.
Attributes in custom components are defined by the developer. They can be required or optional depending on implementation. See the official `MJML` [documentation](https://documentation.mjml.io/#components) for more information.
```js
static allowedAttributes = {
"img-src": "string", // REQUIRED: Source for the image displayed in the right-hand side of the blue header area
@@ -108,7 +111,7 @@ Custom components, such as `mj-bw-hero`, must be defined in the `.mjmlconfig` in
### `mj-include`
You are also able to reference other more static MJML templates in your MJML file simply by referencing the file within the MJML template.
You are also able to reference other more static `MJML` templates in your `MJML` file simply by referencing the file within the `MJML` template.
```html
<!-- Example of reference to mjml template -->
@@ -118,6 +121,6 @@ You are also able to reference other more static MJML templates in your MJML fil
```
#### `head.mjml`
Currently we include the `head.mjml` file in all MJML templates as it contains shared styling and formatting that ensures consistency across all email implementations.
Currently we include the `head.mjml` file in all `MJML` templates as it contains shared styling and formatting that ensures consistency across all email implementations.
In the future we may deviate from this practice to support different layouts. At that time we will modify the docs with direction.

View File

@@ -75,4 +75,4 @@ The `IMailService` automatically uses both versions when sending emails:
- Test plain text templates to ensure they're readable and convey the same message
## `*.mjml`
This is a templating language we use to increase efficiency when creating email content. See the readme within the `./mjml` directory for more comprehensive information.
This is a templating language we use to increase efficiency when creating email content. See the `MJML` [documentation](./Mjml/README.md) for more details.

View File

@@ -1,9 +1,14 @@
# Mail Services
## `MailService`
The `MailService` and its implementation in `HandlebarsMailService` has been deprecated in favor of the `Mailer` implementation.
> [!WARNING]
> The `MailService` and its implementation in `HandlebarsMailService` has been deprecated in favor of the `Mailer` implementation.
New emails should be implemented using [MJML](../../MailTemplates/README.md) and the `Mailer`.
The `MailService` class manages **all** emails, and has multiple responsibilities, including formatting, email building (instantiation of ViewModels from variables), and deciding if a mail request should be enqueued or sent directly.
The resulting implementation cannot be owned by a single team (since all emails are in a single class), and as a result, anyone can edit any template without the appropriate team being informed.
To alleviate these issues, all new emails should be implemented using [MJML](../../MailTemplates/README.md) and the `Mailer`.
## `Mailer`
@@ -16,20 +21,20 @@ The Mailer system consists of four main components:
1. **IMailer** - Service interface for sending emails
2. **BaseMail<TView>** - Abstract base class defining email metadata (recipients, subject, category)
3. **BaseMailView** - Abstract base class for email template view models
3. **BaseMailView** - Abstract base class for email template ViewModels
4. **IMailRenderer** - Internal interface for rendering templates (implemented by `HandlebarMailRenderer`)
### How To Use
1. Define a view model that inherits from `BaseMailView` with properties for template data
2. Create Handlebars templates (`.html.hbs` and `.text.hbs`) as embedded resources, preferably using the MJML pipeline,
`/src/Core/MailTemplates/Mjml`.
3. Define an email class that inherits from `BaseMail<TView>` with metadata like subject
4. Use `IMailer.SendEmail()` to render and send the email
1. Define a ViewModel that inherits from `BaseMailView` with properties for template data.
2. Define an email class that inherits from `BaseMail<TView>` with metadata like `Subject`.
3. Create Handlebars templates (`.html.hbs` and `.text.hbs`) as embedded resources, preferably using the `MJML` [pipeline](../../MailTemplates/Mjml/README.md#development-process), in
a directory in `/src/Core/MailTemplates/Mjml`.
4. Use `IMailer.SendEmail()` to render and send the email.
### Creating a New Email
#### Step 1: Define the Email & View Model
#### Step 1: Define the ViewModel
Create a class that inherits from `BaseMailView`:
@@ -43,17 +48,25 @@ public class WelcomeEmailView : BaseMailView
public required string UserName { get; init; }
public required string ActivationUrl { get; init; }
}
```
#### Step 2: Define the email class
Create a class that inherits from `BaseMail<TView>`:
```csharp
public class WelcomeEmail : BaseMail<WelcomeEmailView>
{
public override string Subject => "Welcome to Bitwarden";
}
```
#### Step 2: Create Handlebars Templates
#### Step 3: Create Handlebars templates
Create two template files as embedded resources next to your view model. **Important**: The file names must be located
directly next to the `ViewClass` and match the name of the view.
Create two template files as embedded resources next to your ViewModel.
> [!IMPORTANT]
> The files must be located directly next to the `ViewClass` and match the name of the view.
**WelcomeEmailView.html.hbs** (HTML version):
@@ -87,7 +100,7 @@ Activate your account: {{ ActivationUrl }}
</ItemGroup>
```
#### Step 3: Send the Email
#### Step 4: Send the email
Inject `IMailer` and send the email, this may be done in a service, command or some other application layer.
@@ -160,7 +173,7 @@ public class MarketingEmail : BaseMail<MarketingEmailView>
### Built-in View Properties
All view models inherit from `BaseMailView`, which provides:
All ViewModels inherit from `BaseMailView`, which provides:
- **CurrentYear** - The current UTC year (useful for copyright notices)
@@ -176,7 +189,7 @@ Templates must follow this naming convention:
- HTML template: `{ViewModelFullName}.html.hbs`
- Text template: `{ViewModelFullName}.text.hbs`
For example, if your view model is `Bit.Core.Auth.Models.Mail.VerifyEmailView`, the templates must be:
For example, if your ViewModel is `Bit.Core.Auth.Models.Mail.VerifyEmailView`, the templates must be:
- `Bit.Core.Auth.Models.Mail.VerifyEmailView.html.hbs`
- `Bit.Core.Auth.Models.Mail.VerifyEmailView.text.hbs`
@@ -210,4 +223,4 @@ services.TryAddSingleton<IMailer, Mailer>();
The mail services support loading the mail template from disk. This is intended to be used by self-hosted customers who want to modify their email appearance. These overrides are not intended to be used during local development, as any changes there would not be reflected in the templates used in a normal deployment configuration.
Any customer using this override has worked with Bitwarden support on an approved implementation and has acknowledged that they are responsible for reacting to any changes made to the templates as a part of the Bitwarden development process. This includes, but is not limited to, changes in Handlebars property names, removal of properties from the `ViewModel` classes, and changes in template names. **Bitwarden is not responsible for maintaining backward compatibility between releases in order to support any overridden emails.**
Any customer using this override has worked with Bitwarden support on an approved implementation and has acknowledged that they are responsible for reacting to any changes made to the templates as a part of the Bitwarden development process. This includes, but is not limited to, changes in Handlebars property names, removal of properties from the ViewModel classes, and changes in template names. **Bitwarden is not responsible for maintaining backward compatibility between releases in order to support any overridden emails.**