Git Product home page Git Product logo

laravel-mail-preview's Introduction

A mail driver to quickly preview mail

Latest Version on Packagist run-tests Software License Total Downloads

This package can display a small overlay whenever a mail is sent. The overlay contains a link to the mail that was just sent.

screenshot

This can be handy when testing out emails in a local environment.

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

You can install the package via composer:

composer require spatie/laravel-mail-preview

Configuring the mail transport

This package contains a mail transport called preview. We recommend to only use this transport in non-production environments. To use the preview transport, change the mailers.smtp.transport to preview in your config/mail.php file:

// in config/mail.php

'mailers' => [
    'smtp' => [
        'transport' => 'preview',
        // ...
    ],
    // ...
],

Registering the preview middleware route

The package can display a link to sent mails whenever they are sent. To use this feature, you must add the Spatie\MailPreview\Http\Middleware\AddMailPreviewOverlayToResponse middleware to the web middleware group in your kernel.

// in app/Http/Kernel.php

protected $middlewareGroups = [
    'web' => [
        // other middleware
        
        \Spatie\MailPreview\Http\Middleware\AddMailPreviewOverlayToResponse::class,
    ],
    
    // ...
];

You must also add the mailPreview to your routes file. Typically, the routes file will be located at routes/web.php.

// in routes/web.php

Route::mailPreview();

This will register a route to display sent mails at /spatie-mail-preview. To customize the URL, pass the URL you want to the macro.

Route::mailPreview('custom-url-where-sent-mails-will-be-shown');

Publishing the config file

Optionally, you can publish the config file with:

php artisan vendor:publish --provider="Spatie\MailPreview\MailPreviewServiceProvider" --tag="mail-preview-config"

This is the content of the config file that will be published at config/mail-preview.php:

return [
    /*
     * By default, the overlay will only be shown and mail will only be stored
     * when the application is in debug mode.
     */
    'enabled' => env('APP_DEBUG', false),

    /*
     * All mails will be stored in the given directory.
     */
    'storage_path' => storage_path('email-previews'),

    /*
     * This option determines how long generated preview files will be kept.
     */
    'maximum_lifetime_in_seconds' => 60,

    /*
     * When enabled, a link to mail will be added to the response
     * every time a mail is sent.
     */
    'show_link_to_preview' => true,

    /*
     * Determines how long the preview pop up should remain visible.
     *
     * Set this to `false` if the popup should stay visible.
     */
    'popup_timeout_in_seconds' => 8,
    
    /**
     * Determines the date format used for the file names.
     *
     * The default 'u' stands for microseconds.
     */
    'filename_date_format' => 'u',
];

Publishing the views

Optionally, you can publish the views that render the preview overlay and the mail itself.

php artisan vendor:publish --provider="Spatie\MailPreview\MailPreviewServiceProvider" --tag="mail-preview-views"

You can modify the views that will be published at resources/views/vendor/mail-preview to your liking.

Usage

Everytime an email is sent, an .html and .eml file will be saved in the directory specified in the storage_path of the mail-preview config file. The name includes the first recipient and the subject:

1457904864_john_at_example_com_invoice_000234.html
1457904864_john_at_example_com_invoice_000234.eml

You can open the .html file in a web browser. The .eml file in your default email client to have a realistic look of the final output.

Preview in a web browser

When you open the .html file in a web browser you'll be able to see how your email will look.

At the beginning of the generated file you'll find an HTML comment with all the message info:

<!--
From:{"[email protected]":"Acme HQ"},
to:{"[email protected]":"Jack Black"},
reply-to:{"[email protected]"},
cc:[{"[email protected]":"Acme Finance"}, {"[email protected]":"Acme Management"}],
bcc:null,
subject:Invoice #000234
-->

Events

Whenever a mail is stored on disk, the Spatie\MailPreview\Events\MailStoredEvent will be fired. It has three public properties:

  • message: an instance of Swift_Mime_SimpleMessage
  • pathToHtmlVersion: the path to the html version of the sent mail
  • pathToEmlVersion: the path to the email version of the sent mail

Making assertions against sent mails

Currently, using Laravel's Mail::fake you cannot make any assertions against the content of a mail, as the using the fake will not render the mail.

The SentMails facade provided this package does allow you to make asserts against the content.

This allows you to make assertions on the content of a mail, without having the mailable in scope.

// in a test

Artisan::call(CommandThatSendsMail::class);

Spatie\MailPreview\Facades\SentMails::assertLastContains('something in your mail');

Let's explain other available assertions method using this mailable as example.

namespace App\Mail;

use Illuminate\Mail\Mailable;

class MyNewSongMailable extends Mailable
{
    public function build()
    {
        $this
            ->to('[email protected]')
            ->cc('[email protected]')
            ->bcc('[email protected]')
            ->subject('Here comes the sun')
            ->html("It's been a long cold lonely winter");
    }
}

In your code you can send that mailable with:

Mail::send(new MyNewSongMailable());

In your tests you can assert that the mail was sent using the assertSent function. You should pass a callable to assertSent which will get an instance of SentMail to it. Each sent mail will be passed to the callable. If the callable returns true the assertions passes.

use Spatie\MailPreview\Facades\SentMails;
use \Spatie\MailPreview\SentMails\SentMail;

SentMails::assertSent(fn (SentMail $mail) => $mail->bodyContains('winter')); // will pass
SentMails::assertSent(fn (SentMail $mail) => $mail->bodyContains('spring')); // will not pass

You can use as many assertion methods on the SentMail as you like.

SentMails::assertSent(function (SentMail $mail)  {
    return
        $mail->subjectContains('sun') &&
        $mail->hasTo('[email protected]')
        $mail->bodyContains('winter');

The Spatie\MailPreview\Facades\SentMails has the following assertions methods:

  • assertCount(int $expectedCount): assert how many mails were sent
  • assertLastContains(string $expectedSubstring): assert that the body of the last sent mail contains a given substring
  • assertSent($findMailCallable, int $expectedCount = 1): explained above
  • assertTimesSent(int $expectedCount, Closure $findMail)
  • assertNotSent(Closure $findMail)

Additionally, the Spatie\MailPreview\Facades\SentMails has these methods:

  • all: returns an array of sent mails. Each item will be an instance of sentMail
  • count(): returns the amount of mails sent
  • last: returns an instance of SentMail for the last sent mail. If no mail was sent null will be returned.
  • lastContains: returns true if the body of the last sent mail contains the given substring
  • timesSent($findMailCallable): returns the amount of mails the were sent and that passed the given callable

The sentMail class provides these assertions:

  • assertSubjectContains($expectedSubstring)
  • assertFrom($expectedAddress)
  • assertTo$expectedAddress)
  • assertCc($expectedAddress)
  • assertBcc($expectedAddress)
  • assertContains($substring): will pass if the body of the mail contains the substring

Additionally, sentMail contains these methods:

  • subject(): return the body of a mail
  • to(): returns all to recipients as an array
  • cc(): returns all cc recipients as an array
  • bcc(): returns all bcc recipients as an array
  • body(): returns the body of a mail
  • subjectContains): returns a boolean
  • hasFrom($expectedAddress): return a boolean
  • hasTo($expectedAddress): return a boolean
  • hasCc($expectedAddress): return a boolean
  • hasBcc($expectedAddress): return a boolean

Changelog

Please see CHANGELOG for more information on what has changed recently.

UPGRADING

Please see UPGRADING for what to do to switch over from themsaid/laravel-mail-preview, and how to upgrade to newer major versions.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

The initial version of this package was created by Mohamed Said, who graciously entrusted this package to us at Spatie.

License

The MIT License (MIT). Please see License File for more information.

laravel-mail-preview's People

Contributors

adrianmrn avatar adrianogl avatar bernardo-campos avatar bjrnblm avatar chefe avatar chrisbjr avatar coderabbi avatar dododedodonl avatar ekateiva avatar faustbrian avatar freekmurze avatar hosmelq avatar jaulz avatar jaybizzle avatar joelmellon avatar laravel-shift avatar mattsparks avatar mikemand avatar mrk-j avatar mustafaaloko avatar riasvdv avatar rudiedirkx avatar samuelnitsche avatar spoyntersmith avatar stefanzweifel avatar sudogetbeer avatar tekord avatar themsaid avatar tvbeek avatar vinkla avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-mail-preview's Issues

Permanently keep the preview link box up

Sometimes I miss the preview box because I've tabbed away to something else. Is the timeout on the preview link necessary? Or could we make it configurable? I'd prefer to have it up there permanently.

I'm Willing to send a PR if you decide on something!

Missing preview files if sent many mails in console command

Hi! The preview driver used to work fine but now have an issue. I have console command which generates 10 mails. When I run the command, preview generated only for 1-4 mails, picked randomly (and they are shuffling on each execution). Sometimes 1, sometimes more than 1.

The most interesting part is there: if I run the command in debug mode and stops on breakpoints, and continue program execution, then previews generating ALMOST for each mail. I have 9 of 10 mails, sometimes 8 of 10. (my fault, I have 8 mails, which means all previews are generated in debug mode)

Log files contain no errors. Mailable class is Queueable.

Driver configuration:

'path' => storage_path('email-previews'),
'maximum_lifetime' => 3600,
'show_link_to_preview' => false,
'middleware' => [],
QUEUE_DRIVER=sync

Laravel 6.15.0, PHP 7.3, Windows 10 x64

Add Laravel 5.5 compatiblity

When installed in a Laravel 5.5 app there's an exception being thrown:

Declaration of Themsaid\MailPreview\PreviewTransport::send(Swift_Mime_Message $message, &$failedRecipients = NULL) must be compatible with Swift_Transport::send(Swift_Mime_SimpleMessage $message, &$failedRecipients = NULL)

This should be fixed 😄

Testing helper

I recently had to test the content of a rendered email and as such ended up building a small and relatively messy assertion around the mail-preview package.

I'd like to tidy it up, would you be open to something like a "InteractsWithMailPreview" trait that would allow assertions to made against rendered emails?

ValueError: Path cannot be empty

Hi there!

Description

I tried to preview some mails on my local environment, and I've got this error when I tried to see previews on the package's route.

https://flareapp.io/share/x7Xk26OP#F37

I looked on Google and I found the same issue when using Laragon. It was something with tmp directory. Unfortunately, I don't use Laragon and those solutions were not solving my problem. I use Valet.

https://laracasts.com/discuss/channels/laravel/error-valueerror-path-cannot-be-empty-in-file

Information

Laravel Version: 9.24.0
PHP Version: 8.1.10
Composer Version: 2.4.1

Thank you!

Issue with EncryptCookies middleware, session not forgotten

Steps to reproduce:

  1. laravel new mail-preview-test
  2. composer require themsaid/laravel-mail-preview
  3. Create a new MailPreviewController with the following contents:
<?php

namespace App\Http\Controllers;

use Mail;

class MailPreviewController extends Controller
{
    public function send()
    {
        Mail::raw('foo', function ($message) {
            $message->to('[email protected]');
        });

        var_dump(session()->all());

        return 'mail sent';
    }
    public function preview()
    {
        var_dump(session()->all());

        return '</body>';
    }
}
  1. Change web.php to:
<?php

Route::get('/', 'MailPreviewController@send');
Route::get('/preview', 'MailPreviewController@preview');
  1. Visit http://mail-preview-test.test/
  2. Visit http://mail-preview-test.test/preview
  3. See that middleware to unset session is not working correctly, the preview link is visible after refreshing

Demo repository: https://github.com/mrk-j/laravel-mail-preview-bug-example

Attribute [mailPreview] does not exist.

Hello,

I added Route::mailPreview() inside routes/web.php and getting the following error:

Attribute [mailPreview] does not exist.

at vendor/laravel/framework/src/Illuminate/Routing/RouteRegistrar.php:93

I already tried clearing the cache.

PHP 7.3.25
laravel 8.27

Laravel 5.4: Call to undefined method Illuminate\Foundation\Application::share()

Installed this package today on a Laravel 5.4 installation. However, when I try to use it, it throws an error, Call to undefined method Illuminate\Foundation\Application::share().

Indeed, from the Laravel 5.4 upgrade guide:

share Method Removed

The share method has been removed from the container. This was a legacy method that has not been documented in several years. If you are using this method, you should begin using the singleton method instead

share is used in the MailProvider. However, changing it directly to singleton without changing anything else also doesn't work: Container.php line 988: Illegal offset type in unset.

Unsupported mail transport [preview]

Hi, after upgrading Laravel to 8 (8.10.0) I am getting an error:

In MailManager.php line 172:
Unsupported mail transport [preview].

Any idea what might be wrong? I have tried republishing the config file. I am using the latest laravel-mail-preview 4.0

Wrong Github Tags?

Hi themsaid,

do you think localization, translation are good Tags for this package? ^^

greetings

No From, To, Cc and other addresses in HTML file

I do not get addresses in a .html mail file. This is what I see as MessageInfo

<!--
From:[{}], 
to:[{}], 
reply-to:[{}], 
cc:[], 
bcc:[], 
subject:Test subject
-->

When I dump $message->getOriginalMessage()->getFrom(), I get:

array:1 [
  0 => Symfony\Component\Mime\Address^ {#3012
    -address: "[email protected]"
    -name: "Example test (no-reply)"
  }
]

And when it is serialized using json_encode, it becomes: "[{}]"

Laravel version: Laravel Framework 10.18.0

Prob with Laravel 5.1

Hey, Mohamed, I have used this package this may laravel 5.1 app but it generate an error when I typed: composer require ... any ideas?

Race condition with css inliner

Not sure whose bug it is, maybe fedeisas/laravel-mail-css-inliner's.

css inliner's service provider (in register()):

$this->app->afterResolving('mail.manager', function (MailManager $mailManager) {
    $mailManager->getSwiftMailer()->registerPlugin($this->app->make(CssInlinerPlugin::class));
    return $mailManager;
});

and mail preview's (in boot()):

$this->app['mail.manager']->extend('preview', function () {
    return new PreviewTransport(
        $this->app->make('Illuminate\Filesystem\Filesystem'),
        $this->app['config']['mailpreview.path'],
        $this->app['config']['mailpreview.maximum_lifetime']
    );
});

So mail preview is directly accessing (and building) mail.manager, which immeditately triggers css inliner's afterResolving, which triggers immediately triggers createTransport(), with a transport preview that doesn't exist yet, because mail preview's extend() hasn't even run yet.

Moving mail preview's extend() into a afterResolving('mail.manager', ...) doesn't help, because when the mail manager is finally created (not on every request, just when sending mail) the same race condition happens.

This wasn't a problem with css inliner 2.2 + themsaid/laravel-mail-preview. Maybe because it was a mailer then, not a transport. No idea how to fix this. Typical DI race condition.

Ray in controller

Hi There,

In the following line of code, and the one after that there are calls to the "ray" function, but that function is not available.
link to code where issue occurs

I just clean installed this package into my project (PHP8/L8) and got the following error:

Error

Call to undefined function Spatie\MailPreview\Http\Controllers\ray()
https://standaard-cms-v3.test/spatie-mail-preview?file_type=eml&mail_preview_file_name=969172_subject

Session destroyed

I have a tiny problem with your package unfortunately :(

Every time when i click the link in the "popup" to the email preview and go back to my application my user is logged out.
Can't imagine that this is supposed to happen...

Normally i open the previews directly from the storage folder, but sometimes clicking would be useful.

I hope you can help me out with this annoying thing!

Problems with errors

Hey i am try to use the package but when i abort to personalize the errors page i get this error.
captura de pantalla 22

Route caching

Hello Themsaid,

First of all big thumbs up for your package, really like it!
I am used to cache my routes in production, but with your package its currently not possible to do.

[LogicException]
Unable to prepare route [themsaid/mail-preview] for serialization. Uses Closure.

Do you have a fix for this or any plans to change this in the future?

L5.4 supported yet?

Just wondering if this has full L5.4 support yet? I've installed it in my L5.4 project and although there are no apparent errors, its not creating the html files.

Document middleware property

Except for output location and maximum life time, perhaps it should be mentioned something about middleware property too. It took me some time to figure out why the preview link was always logging me out.

Useful package. Thanks!

Error when To header is empty

Hi, I am sending an email with only "BCC", then an error occurs as the "To" header is empty.
The error happens when the preview file name is generated. Here is the line causing it

A possible solution would be to use the "CC" and "BCC" to generate the filename.

        $to = '';
        foreach ([$message->getTo(), $message->getCc(), $message->getBcc()] as $receipents) {
            if (!empty($receipents)) {
                $to = str_replace(['@', '.'], ['_at_', '_'], array_keys($receipents)[0]);
                break;
            }
        }

I will be happy to make PR. Let me know if you have better idea.

Thanks.

Attachments missing in .eml file

When I'm creating a Mailable and attach files to it in the build() method, the created .eml file by mail-preview during the send mechanism is missing those attachments.

When I use mailtrap I have the attachments. Am I missing something, or are attachments not supported?

Session store not set on request

in /home/vagrant/mt2magic-com/vendor/laravel/framework/src/Illuminate/Http/Request.php line 859
at Request->session() in /home/vagrant/mt2magic-com/vendor/themsaid/laravel-mail-preview/src/MailPreviewMiddleware.php line 27
at MailPreviewMiddleware->handle(object(Request), object(Closure))

I keep getting "Session store not set on request"

Add mail.preview config to allow simpler conditional enabling, open door to other enhancments.

Would you consider a pull request to change the logic of laravel-mail-preview/src/MailProvider.php to call $this->registerPreviewSwiftMailer() if either the current condition is met or $this->app['config']['mail.preview'] == true?

The rationale is that by providing a config key which can enable mail previews without replacing the mail driver, (1) it becomes simpler to condition mail previews upon the environment, APP_DEBUG value, or similar (certainly possible as is, but more cumbersome), and (2) you open the door to enhancements akin to that in #7 (currently closed, I realize).

I'll provide the PR, just wanted to confirm it would be well received first. Thanks!

Driver [preview] not supported

I'm using Laravel 5.8.

I change my env file to with MAIL_DRIVER=preview

I run composer install + publish the config file before.

But when I try to send an email I have an error said that the preview driver is not supported.

Can anyone help ?

Attribute [mailPreview] does not exist

I added Route::mailPreview() inside routes/web.php and getting the following error:

Attribute [mailPreview] does not exist.

at vendor/laravel/framework/src/Illuminate/Routing/RouteRegistrar.php:93

the part in the envoy blade where it occures is:

@task('optimizeInstallation', ['on' => 'remote'])
{{ logMessage('✨  Optimizing installation...') }}
cd {{ $newReleaseDir }};

{{ logMessage('✨  clear-compiled...') }}
php -d disable_functions='' artisan clear-compiled;
{{ logMessage('✨  artisan optimize..') }}
php -d disable_functions='' artisan optimize;
@endtask

=> artisan clear-compiled

I don't know if this is an envoy issue or this package issue.

PHP 8.0.2
Laravel 8.28.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.