visualbuilder/email-templates

Email Template editor for Filament


README

Latest Version on Packagist Packagist Downloads run-tests GitHub last commit

Email Preview

Why businesses and applications should use Email Templates

  • Time-saving: Email templates eliminate the need to create emails from scratch, saving valuable time and effort.
  • Customisability: Quick editing capabilities enable employees to personalise the content of the templates while maintaining a professional appearance.
  • Consistent branding: Templates ensure that all emails adhere to the brand's guidelines, reinforcing brand recognition and professionalism.
  • Professional appearance: Well-designed templates provide a polished and consistent look, enhancing the business's credibility and reputation.
  • Streamlined communication: Prompt and efficient communication.
  • Flexibility: Templates can be adapted for various purposes, such as promotional emails, customer support responses, newsletters, and more.
  • Easy updates: Templates can be easily modified to reflect changes in offers, policies, or design elements, ensuring that communication remains current and aligned with business objectives.
  • Standardisation: Templates enforce a standardized structure and format for emails, reducing errors and improving clarity in communication.
  • Scalability: Email templates facilitate consistent messaging even as the business grows, ensuring a cohesive customer experience across all interactions.
  • Improved productivity: With quick access to templates, employees can focus more on core tasks, increasing overall productivity within the business.

This package provides:-

  • Content management for email templates allowing authorised users to edit email template content in the admin.
  • Templates can include model attribute tokens or config values which will be replaced, eg ##user.name## or ##config.app.name##
  • Templates can be saved with different locales for multi-lingual capability.
  • A generic method for quickly creating mail classes to speed up adding new templates and faster automation possiblities.
  • Theme editor - Set your own colours and apply to specific templates.

We use the standard Laravel mail sending capability, the package simply allows content editing and faster adding of new template Classes

Theme Editor

Email Preview

HTML Email Template Editor

Edit email content in the admin and use tokens to inject model or config content.

Email Preview

Installation

Get the package via composer:

composer require visualbuilder/email-templates

Running the install command will copy the template views, migrations, seeders and config file to your app.

The --seed option will populate 7 default templates which you can then edit in the admin panel.

 php artisan filament-email-templates:install --seed

Note: The seeder can also be edited directly if you wish to prepopulate with your own content. database\Seeders\EmailTemplateSeeder.php

Adding the plugin to a panel

Add this plugin to panel using plugins() method in app/Providers/Filament/AdminPanelProvider.php:

use Visualbuilder\EmailTemplates\EmailTemplatesPlugin;
 
public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugins([
            EmailTemplatesPlugin::make(),
            // ...
        ]);
}

Menu Group and sort order can be set in the config

Enabling navigation

In the config file config/filament-email-templates.php navigation can be disabled/enabled

    /**
     * Admin panel navigation options
     */
    'navigation' => [
        'enabled' => true,
        'templates' => [
            'sort' => 10,
            'label' => 'Email Templates',
            'icon' => 'heroicon-o-envelope',
            'group' => 'Content',
            'cluster' => false,
            'position' => SubNavigationPosition::Top
        ],
        'themes' => [
            'sort' => 20,
            'label' => 'Email Template Themes',
            'icon' => 'heroicon-o-paint-brush',
            'group' => 'Content',
            'cluster' => false,
            'position' => SubNavigationPosition::Top
        ],
    ],

Or you can use a closure to enable navigation only for specific users:

// AdminPanelProvider.php
    ->plugins([
// ...
        EmailTemplatesPlugin::make()
                ->enableNavigation(
                    fn () => auth()->user()->can('view_email_templates') || auth()->user()->can('view_any_email_templates)'),
               ),
    ])

Usage

Tokens

Token format is ##model.attribute##. When calling the email pass any referenced models to replace the tokens automatically.

You can also include config values in the format ##config.file.key## eg ##config.app.name##.

In the email templates config file you must specify keys that are allowed to be replaced.

    /**
     * Allowed config keys which can be inserted into email templates
     * eg use ##config.app.name## in the email template for automatic replacement.
     */
    'config_keys' => [
        'app.name',
        'app.url',
        'email-templates.customer-services'

Implementing out of the box templates

Emails may be sent directly, via a notification or an event listener.

The following email templates are included to get you started and show different methods of sending.

  • User Registered - Welcome them to the platform
  • User Verify Email - Check they are human
  • User Verified Email - Yes they are
  • User Request Password Reset - Let them change the password
  • User Password Reset Success - Yay, you changed your password
  • User Locked Out - Oops - What to do now?
  • User Login - Success

Not all systems will require a login notification, but it's good practice for security so included here.

New User Registered Email

A new Registered event is triggered when creating a new user.

We want to welcome new users with a friendly email so we've included a listener for the Illuminate\Auth\Events\Registered Event which will send the email if enabled in the config:-

  'send_emails'             => [
        'new_user_registered'    => true,
        'verification'           => true,
        'user_verified'          => true,
        'login'                  => true,
        'password_reset_success' => true,
    ],

User Verify Email

This notification is built in to Laravel so we have overidden the default toMail function to use our custom email template.

For reference this is done in the EmailTemplatesAuthServiceProvider.

This can be disabled in the config.

To Enable email verification ensure the User model implements the Laravel MustVerifyEmail contract:-

class User extends Authenticatable implements MustVerifyEmail

and include the verified middleware in your routes.

User Request Password Reset

Another Laravel built in notification, but to enable the custom email just add this function to your authenticatable user model.

use Visualbuilder\EmailTemplates\Notifications\UserResetPasswordRequestNotification;

/**
     * @param $token
     *
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $url = \Illuminate\Support\Facades\URL::secure(route('password.reset', ['token' => $token, 'email' =>$this->email]));

        $this->notify(new UserResetPasswordRequestNotification($url));
    }

Customising the email template

Some theme colour options have been provided. Email templates will use the default theme unless you specify otherwise on the email template.

In the config file config/filament-email-templates.php logo, contacts, links and admin preferences can be set

    //Default Logo
    'logo'                    => 'media/email-templates/logo.png',

    //Logo size in pixels -> 200 pixels high is plenty big enough.
    'logo_width'              => '476',
    'logo_height'             => '117',

    //Content Width in Pixels
    'content_width'           => '600',

    //Contact details included in default email templates
    'customer-services'  => ['email' => 'support@yourcompany.com',
                             'phone' => '+441273 455702'],

    //Footer Links
    'links'                   => [
        ['name' => 'Website', 'url' => 'https://yourwebsite.com', 'title' => 'Goto website'],
        ['name' => 'Privacy Policy', 'url' => 'https://yourwebsite.com/privacy-policy', 'title' => 'View Privacy Policy'],
    ],

If you wish to directly edit the template blade files, see the primary template here:

  • Path: resources/views/vendor/vb-email-templates/email/default.php

New templates in this directory will be automatically visible in the email template editor dropdown for selection.

Useful Tip

Not all email clients (e.g., Outlook) render CSS from a stylesheet effectively. To ensure maximum compatibility, it's best to put styles inline. For checking how your email looks across different clients, Litmus Email Previews is highly recommended.

Translations

Each email template is identified by a key and a language:

  • Key: user-password-reset
  • Language: en_gb

This allows the relevant template to be selected based on the users locale - You will need to save the users preferred language to implement this.

Please note laravel default locale is just "en" we prefer to separate British and American English so typically use en_GB and en_US instead but you can set this value as you wish.

Languages that should be shown on the language picker can be set in the config

    'default_locale'   => 'en_GB',

    //These will be included in the language picker when editing an email template
    'languages'        => [
        'en_GB' => ['display' => 'British', 'flag-icon' => 'gb'],
        'en_US' => ['display' => 'USA', 'flag-icon' => 'us'],
        'es'    => ['display' => 'Español', 'flag-icon' => 'es'],
        'fr'    => ['display' => 'Français', 'flag-icon' => 'fr'],
        'in'    => ['display' => 'Hindi', 'flag-icon' => 'in'],
        'pt'    => ['display' => 'Brasileiro', 'flag-icon' => 'br'],
    ]

Language Picker

Flag icons are loaded from CDN: https://cdn.jsdelivr.net/gh/lipis/flag-icons@6.6.6/css/flag-icons.min.css see https://www.npmjs.com/package/flag-icons

Creating new Mail Classes

We've currently opted to keep using a separate Mailable Class for each email type. This means when you create a new template in the admin, it will require a new php Class. The package provides an action to build the class if the file does not exist in app\Mail\VisualBuilder\EmailTemplates.

Build Class Currently generated Mailable Classes will use the BuildGenericEmail Trait

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Visualbuilder\EmailTemplates\Traits\BuildGenericEmail;

class MyFunkyNewEmail extends Mailable
{
    use Queueable, SerializesModels, BuildGenericEmail;

    public string $template = 'email-template-key';  //Change this to the key of the email template content to load
    public string $sendTo;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($user) {
        $this->sendTo = $user;
    }
}

Including other models in the email for token replacement

Just pass through the models you need and assign them in the constructor.

class MyFunkyNewEmail extends Mailable
{
    use Queueable, SerializesModels, BuildGenericEmail;

    public string $template = 'email-template-key';  //Change this to the key of the email template content to load
    public string $sendTo;
    public Model $booking;

    public function __construct($user, Booking $booking) {
            $this->user       = $user;
            $this->booking    = $booking;
            $this->sendTo     = $user->email;
        }

In this example you can then use ##booking.date## or whatever attributes are available in the booking model.

If you need to derive some attribute you can add Accessors to your model.

Both of these function will allow you to use:-

##user.full_name## in the email template:-

public function getFullNameAttribute()
{
  return $this->firstname.' '.$this->lastname;
}

OR

protected function fullName(): Attribute
{
    return Attribute::make(
        get: fn () => $this->firstname.' '.$this->lastname,
    );
}

Adding Attachments

In here you can see how to pass an attachment:-

The attachment should be passed to the Mail Class and set as a public property.

In this case we've passed an Order model and an Invoice model which has a PDF.

class SalesOrderEmail extends Mailable
{
    use Queueable, SerializesModels, BuildGenericEmail;

    public string $template = 'email-template-key';
    public string $sendTo;
    public $attachment;
    public User $user;
    public Order $order;
    public Invoice $invoice;

    /**
     * Constructor for SalesOrderEmail.
     *
     * @param User $user User object
     * @param Order $order Order object
     * @param Invoice $invoice Invoice object
     */
    public function __construct($user, $order, $invoice) {
        $this->user = $user;
        $this->order = $order;
        $this->invoice = $invoice;
        $this->attachment = $invoice->getPdf(); // Missing semicolon added
        $this->sendTo = $user->email;
    }
}

*** Update *** From php8.0 the above code can be shortend to:_

class SalesOrderEmail extends Mailable
{
    use Queueable, SerializesModels, BuildGenericEmail;

    public string $template = 'email-template-key'; 
    public string $sendTo;
    public $attachment;

    /**
     * Constructor for SalesOrderEmail using PHP 8 constructor property promotion.
     *
     * @param User $user User object
     * @param Order $order Order object
     * @param Invoice $invoice Invoice object
     */
    public function __construct(public User $user, public Order $order, public Invoice $invoice) {
        $this->attachment = $invoice->getPdf(); 
        $this->sendTo = $user->email;
    }
}

The attachment is handled in the build function of the BuildGenericEmail trait. Customise the filename with attachment->filename You should also include the filetype.

 public function build() {
        $template = EmailTemplate::findEmailByKey($this->template, App::currentLocale());

        if($this->attachment ?? false) {
            $this->attach(
                $this->attachment->filepath, [
                'as'   => $this->attachment->filename,
                'mime' => $this->attachment->filetype
            ]
            );
        }

        $data = [
            'content'       => TokenHelper::replace($template->content, $this),
            'preHeaderText' => TokenHelper::replace($template->preheader, $this),
            'title'         => TokenHelper::replace($template->title, $this)
        ];

        return $this->from($template->from['email'],$template->from['name'])
            ->view($template->view_path)
            ->subject(TokenHelper::replace($template->subject, $this))
            ->to($this->sendTo)
            ->with(['data'=>$data]);
    }

To maximise compatibility we've kept with the L9 mailable methods -> which still work on L10.

Testing

./vendor/bin/pest      

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email support@ekouk.com instead of using the issue tracker.

Credits

License

The GNU GPLv3. Please see License File for more information.