Important
You are browsing upcoming documentation for version 6.1 of OroCommerce, scheduled for release in 2025. Read the documentation for version 6.0 (the latest LTS version) to get up-to-date information.
See our Release Process documentation for more information on the currently supported and upcoming releases.
Configure Email Templates in the Back-Office
With the Oro applications, you can easily send numerous personalized emails using one template. For example, you can make a single template that welcomes {username}, assign it to an email campaign, and each of your subscribers will get a mail sent specifically to them.
Create a New Email Template
Navigate to System > Emails > Templates.
Click Create Template.
Define the general settings for the template.
The following fields are mandatory and must be defined:
Owner — Limits the list of users that can manage the template, subject to the access and permission settings.
Template Name — Name used to refer to the template in the system.
Type — Use html or plain text.
Entity Name — The field is optional and is used to define an entity, variables whereof can be used in the template. If no entity name is defined, only system variables are available.
Important
If you want to use the template for autoresponses, the Entity Name field value should be Email.
Website — Choose a website for which this template applies, or leave it blank if the template should be applicable to all websites. To apply this template customization to a different website, clone the template and define the necessary website. Ensure the original template name remains unchanged for the template to work correctly.
Under Template Data, define the email template. Click on the necessary variable to add it to the text box.
Note
If you prefer working with email templates via the WYSIWYG editor, you can enable it globally or per organization. However, remember that the WYSIWYG editor is incompatible with the default base email template. Enabling it may break existing email templates and prevent them from being saved. Therefore, it is disabled by default.
Click Save to apply the changes.
You can delete , edit , and clone email templates on the page of all templates.
Important
Keep in mind that the ability to view, edit, clone, or delete email templates depends on specific roles and permissions defined in the system configuration. For more information about available access levels and permissions, see the Understand Roles and Permissions guide.
Inherit Base Email Template
Email template inheritance allows developers to maintain common styles and foundational markup in a single base template, which can then be extended or modified for different email types without repetitive copying and pasting. This base template includes common styles, headers, footers, and foundational markup, ensuring consistency across different emails. Users or frontend developers can manage the base template; the system will first search for it by name among user-managed templates, and if not found, will check the storefront theme.
You can have multiple base templates with different designs within the system, specified through a special markup tag, for instance:
{% extends oro_get_email_template('base_storefront') %}
where base_storefront
is a name of the base email template whose styles and markup you want to re-use.
Here is the OroCommerce’s default base_storefront email template that contains a common markup.
Several existing templates already use this inheritance principle with the mentioned code block:
{% extends oro_get_email_template('base_storefront') %}
The website from which the request is sent is detected automatically from the related entity of an email template. If you need to specify the website, you can use the additional context argument:
{% extends oro_get_email_template('base', { website: entity.website }) %}
While using inheritance, the text or HTML should only be within the blocks defined in the parent template, for example:
{% extends oro_get_email_template('base_storefront') %}
No tags or text are allowed
{% block content %}
Any text or code is allowed
{% endblock %}
No tags, text or code are allowed
Note
The WYSIWYG editor is incompatible with the inherited email templates. Enabling it may break existing email templates and prevent them from being saved. Therefore, it is disabled by default.
View Available Template Variables and Functions
For the security reasons, the only available variables are currently the ones listed on the Email Templates edit page in the back-office. However, there are a few exceptions. These are several Twig functions enabled in email templates that can be used to obtain some specific data in email templates.
The full list of these functions is the following:
Important
Keep in mind that when editing HTML email templates, you pass them to the WYSIWYG editor. WYSIWYG automatically tries to modify the given HTML template against the HTML specifications. Therefore, the text and tags that violate the HTML specifications should be wrapped up in the HTML comment. For example, there should not be any other tags or text between the <table></table> tags except thead, tbody, tfoot, th, tr, td.
Examples:
Invalid template:
<table>
<thead>
<tr>
<th><strong>Acme</strong></th>
</tr>
</thead>
{% for item in collection %}
<tbody>
{% for subItem in item %}
<tr>
{% if loop.first %}
<td>{{ subItem.key }}</td>
<td>{{ subItem.value }}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
{% endfor %}
</table>
Valid template:
<table>
<thead>
<tr>
<th><strong>Acme</strong></th>
</tr>
</thead>
<!--{% for item in collection %}-->
<tbody>
<!--{% for subItem in item %}-->
<tr>
<!--{% if loop.first %}-->
<td>{{ subItem.key }}</td>
<td>{{ subItem.value }}</td>
<!--{% endif %}-->
</tr>
<!--{% endfor %}-->
</tbody>
<!--{% endfor %}-->
</table>
Functions
date
date()
Description: Converts an argument into a date to allow date comparison.
Example: See the Twig doc
oro_config_value
oro_config_value(string $configSettingName)
Description: Returns the Oro config setting value.
Returns: string
calendar_date_range
calendar_date_range(
\DateTime $startDate = null,
\DateTime $endDate = null,
$skipTime = false,
$dateType = null, \\IntlDateFormatter constant or it's string name
$timeType = null, \\IntlDateFormatter constant or it's string name
$locale = null,
$timeZone = null
)
Description: Returns a string that represents a range between $startDate and $endDate, formatted according the given parameters.
Returns: string
- $endDate is not specified
Thu Oct 17, 2013 - when $skipTime = true
Thu Oct 17, 2013 5:30pm - when $skipTime = false
- $startDate equals to $endDate
Thu Oct 17, 2013 - when $skipTime = true
Thu Oct 17, 2013 5:30pm - when $skipTime = false
- $startDate and $endDate are the same day
Thu Oct 17, 2013 - when $skipTime = true
Thu Oct 17, 2013 5:00pm – 5:30pm - when $skipTime = false
- $startDate and $endDate are different days
Thu Oct 17, 2013 5:00pm – Thu Oct 18, 2013 5:00pm - when $skipTime = false
Thu Oct 17, 2013 – Thu Oct 18, 2013 - when $skipTime = true
Example:
calendar_date_range(entity.start, entity.end, entity.allDay, 'F j, Y', 1)
calendar_date_range_organization
calendar_date_range_organization(
\DateTime $startDate = null,
\DateTime $endDate = null,
$skipTime = false,
$dateType = null, \\IntlDateFormatter constant or it's string name
$timeType = null, \\IntlDateFormatter constant or it's string name
$locale = null,
$timeZone = null,
OrganizationInterface $organization = null
)
Description: The same as ‘calendar_date_range’ but for a specific organization.
Returns: string
get_event_recurrence_pattern
get_event_recurrence_pattern(Entity\CalendarEvent $event)
Description: This method is aimed at showing text description of a recurring event in email invitations.
Returns: string
Example:
<!--{% if get_event_recurrence_pattern(entity) %}-->
<tr>
<td style="width: 65pt; padding: 0 5pt 0 9pt; line-height: 1.3em; font-family: Arial, Helvetica, sans-serif; font-size: 10pt; vertical-align: top"><strong>Repeats:</strong></td>
<td style="padding: 0 9pt 0 0; line-height: 1.3em; font-family: Arial, Helvetica, sans-serif; font-size: 10pt; vertical-align: top">{{ get_event_recurrence_pattern(entity) }}</td>
</tr>
<!--{% endif %}-->
website_path
website_path(string $route, array $routeParams, Website|null $website = null)
Description: Returns the absolute path for a specific route and parameters.
Returns: string
Example:
<p>Please follow this link to confirm your email address: <a href="{{ website_path('oro_customer_frontend_customer_user_confirmation', {'username': entity.username, 'token': token}, entity.website) }}">Confirm</a></p>
website_secure_path
website_secure_path()
Description: The same as ‘website_path’ but returns Secure (HTTPS) URL.
url
url()
Description: Returns the absolute URL (with scheme and host) for the given route.
Example: See the Symfony Twig Extensions (URL)
path
path()
Description: Returns the relative URL (without the scheme and host) for the given route.
Example: See the Symfony Twig Extensions (Path)
get_payment_methods (OroCommerce Only)
get_payment_methods(Order $entity)
Description: Returns the allowed payment methods for orders.
Returns: array of Oro\Bundle\PaymentBundle\Twig\DTO\PaymentMethodObject objests with payment method label and options (can be used as array of strings thanks to PaymentMethodObject::__toString method)
Example:
{% set payment_methods = get_payment_methods(entity) %}
<!--{% if payment_methods|length == 1 %}-->
<h4>Payment Method:</h4>
<!--{{ payment_methods[0] }}-->
<!--{% elseif payment_methods|length > 1 %}-->
<h4>Payment Methods:</h4>
<!--{{ payment_methods|join(', ') }}-->
<!--{% endif %}-->
Alternative example:
{% set payment_methods = get_payment_methods(entity) %}
{% if payment_methods|length == 1 %}
<strong>Payment Method: </strong>
{% elseif payment_methods|length > 1 %}
<strong>Payment Methods: </strong>
{% endif %}
{% for payment_method in payment_methods %}
{{ payment_method.label }}
{% if payment_method.options|length > 0 %}
{{- ' (' ~ payment_method.options|join(', ') ~ ')' -}}
{% endif %}
<br/>
{% endfor %}
get_payment_status_label (OroCommerce only)
get_payment_status_label(Order $entity)
Description: Returns the translated label for the payment status.
See the \Oro\Bundle\PaymentBundle\Formatter\PaymentStatusLabelFormatter::formatPaymentStatusLabel
method for details.
Returns: string
Example: See an example for the get_payment_status (OroCommerce only) method.
get_payment_status (OroCommerce only)
get_payment_status(Order $entity)
Description: Returns the payment status for requested order.
Returns: string, one of Oro\Bundle\PaymentBundle\Provider\PaymentStatusProvider class statuses (‘full’, ‘partially’, ‘invoiced’, ‘authorized’, ‘declined’, ‘pending’)
Example:
<strong>Payment Status: </strong>{{ get_payment_status_label(get_payment_status(entity)) }}
oro_order_shipping_method_label (OroCommerce only)
oro_order_shipping_method_label(?string $shippingMethod, ?string $shippingMethodType, ?Organization $organization = null)
Description: Returns the translated label of the order`s shipping method.
Returns: string
Example:
{% set shipping_method = oro_order_shipping_method_label(entity.shippingMethod, entity.shippingMethodType, entity.organization) %}
<strong>Shipping Method: </strong>{{ shipping_method }}<br/>
rfp_products (OroCommerce Only)
rfp_products(Oro\Bundle\RFPBundle\Entity\Request $entity)
Description: Returns a list of products requested by a buyer in this request for quote.
Returns:
array: [
345 => [ \\Product ID
'name' => 'Yarn', \\ string, Product name
'sku' => 'YRN345erer', \\ string, Product SKU
'comment' => 'Most interesting product for us', \\ string, Comment in RFP for this product
'items' => [ \\array, Requested product items
38473 => [ \\Product Item ID
'quantity' => 49.86, \\ float, Product Item quantity
'price' => $price, \\ \Oro\Bundle\CurrencyBundle\Entity\Price object, Product Item price
'unit' => 'ft', \\ string, Product Item unit
],
139473 => [ \\Product Item ID
'quantity' => 21.98, \\ float, Product Item quantity
'price' => $price, \\ \Oro\Bundle\CurrencyBundle\Entity\Price object, Product Item price
'unit' => 'item', \\ string, Product Item unit
],
...
],
],
...
]
Example:
{% set products = rfp_products(entity) %}
{% if products|length %}
<table style="border: 1px solid black;margin-top: 10px">
<thead>
<tr>
<th><strong>SKU</strong></th>
<th><strong>Product</strong></th>
<th><strong>Quantity</strong></th>
<th><strong>Target Price</strong></th>
<th><strong>Comment</strong></th>
</tr>
</thead>
<!--{% for product in products %}-->
<!--{% set numItems = product.items|length %}-->
<tbody>
<!--{% for item in product.items %}-->
<tr>
<!--{% if loop.first %}-->
<td rowspan="{{ numItems }}">{{ product.sku }}</td>
<td rowspan="{{ numItems }}">{{ product.name }}</td>
<!--{% endif %}-->
<td>{{ item.quantity }} {{ item.unit }}</td>
<td>{{ item.price ? item.price|oro_format_price : '' }}</td>
<!--{% if loop.first %}-->
<td rowspan="{{ numItems }}">{{ product.comment }}</td>
<!--{% endif %}-->
</tr>
<!--{% endfor %}-->
</tbody>
<!--{% endfor %}-->
</table>
{% endif %}
line_items_discounts (OroCommerce only)
line_items_discounts(Order $entity)
Description: Returns array of discount total sum for every order line item.
Returns: array
array: [
'123123' => [ // Line Item ID
'value' => 15.065, \\ float, total sum of discount for the line item
'currency' => 'USD', \\ string, Currency of discount
],
...
]
Example: See an example for order_line_items (OroCommerce only) method.
order_line_items (OroCommerce only)
order_line_items(\Oro\Bundle\OrderBundle\Entity\Order $entity)
Description: Returns order line items.
Returns:
array: [
'lineItems' => [
[
'product_name' => 'Boat', \\ string, Product name
'product_sku' => 'BO1BIG', \\ string, Product SKU
'quantity' => 1.00, \\ float, Product quantity
'unit' => 'item', \\ string, Product unit
'price' => $price, \\ \Oro\Bundle\CurrencyBundle\Entity\Price object, Product price
'comment' => 'Comment for this line Item', string
'ship_by' => $price, \\ \DateTime, Line item ship by field
'id' => $id, \\ int|string, Line item identifier value
'subtotal' => $price, \\\Oro\Bundle\CurrencyBundle\Entity\Price object, Line Item subtotal
],
...
],
'subtotals' => [
[
'label' => 'Shipping', \\ string, Subtotal name
'totalPrice' => $price, \\\Oro\Bundle\CurrencyBundle\Entity\Price object, Subtotal Price
],
...
],
'total' => [
'label' => 'Total', \\ string, label for Total
'totalPrice' => $price, \\\Oro\Bundle\CurrencyBundle\Entity\Price object, Total sum Price for all order line items
],
]
Example:
<table style="border: 1px solid black;margin-top: 10px">
<thead>
<tr>
<th><strong>Item</strong></th>
<th><strong>Quantity</strong></th>
<th><strong>Price</strong></th>
<th><strong>Subtotal</strong></th>
<th><strong>Ship By</strong></th>
<th><strong>Notes</strong></th>
</tr>
</thead>
<tbody>
<!--{% set data = order_line_items(entity) %}-->
<!--{% set lineItemDiscounts = line_items_discounts(entity) %}-->
<!--{% for item in data.lineItems %}-->
<tr>
<td>
{{ item.product_name }}
<br>
SKU #: {{ item.product_sku }}
<br>
</td>
<td>{{ item.quantity|oro_format_short_product_unit_value(item.unit) }}</td>
<td>{{ item.price|oro_format_price }}</td>
<td>
{{ item.subtotal|oro_format_price }}
{% set matchedDiscount = lineItemDiscounts[item.id] %}
{% if matchedDiscount is not null and matchedDiscount.value > 0 %}
<br/>{{ (-matchedDiscount.value)|oro_format_currency({'currency': matchedDiscount.currency}) }}
{% endif %}
</td>
<td>{{ item.ship_by }}</td>
<td>{{ item.comment }}</td>
</tr>
<!--{% endfor %}-->
</tbody>
</table>
Note
If none of these Twig functions cover your cases, you can create a custom Twig function that returns the desired data that you can use in email templates. Please, see OroEmailBundle documentation for details.
Filters
On top of functions, you can use filters in email templates. The full set of these filters is the following:
oro_format_datetime_organization
Localize Email Templates
To create email templates for different localizations, even the inactive ones, move from tab to tab and create the desired content for required localizations.
To enable the email template fallback to the parent localization, select the Use <localization> (Parent Localization) checkbox. If the localization does not have a parent, you can enable fallback to the default template value.
Business Tip
Interested in learning more about B2B eCommerce and how it differs from B2C? Read our in-depth information on the subject.