Laravel 5.4^ - How to customize notification email layout? - laravel

I am trying to customize the HTML email layout that is used when sending notifications via email.
I have published both the mail and notification views.
php artisan vendor:publish --tag=laravel-mail
php artisan vendor:publish --tag=laravel-notifications
If I modify the /resources/views/vendor/notifications/email.blade.php file, I can only change the BODY content of the emails that get sent. I am looking to modify the footer, header, and every other part of the email layout as well.
I tried also modifying the views inside /resources/vendor/mail/html/, but whenever the notification gets sent, it is not even using these views and instead uses the default laravel framework ones.
I am aware I can set a view on the MailMessage returned by my Notification class, but I want to keep the standard line(), greeting(), etc. functions.
Does anyone know how I can get my notifications to send email using the views in /resources/vendor/mail/html ?
The following is my /resources/views/vendor/notifications/email.blade.php file, but it does not have anywhere to customize the header/footer/ overall layout.
#component('mail::message')
{{-- Greeting --}}
#if (! empty($greeting))
# {{ $greeting }}
#else
#if ($level == 'error')
# Whoops!
#else
# Hello!
#endif
#endif
{{-- Intro Lines --}}
#foreach ($introLines as $line)
{{ $line }}
#endforeach
{{-- Action Button --}}
#if (isset($actionText))
<?php
switch ($level) {
case 'success':
$color = 'green';
break;
case 'error':
$color = 'red';
break;
default:
$color = 'blue';
}
?>
#component('mail::button', ['url' => $actionUrl, 'color' => $color])
{{ $actionText }}
#endcomponent
#endif
{{-- Outro Lines --}}
#foreach ($outroLines as $line)
{{ $line }}
#endforeach
<!-- Salutation -->
#if (! empty($salutation))
{{ $salutation }}
#else
Regards,<br>{{ config('app.name') }}
#endif
<!-- Subcopy -->
#if (isset($actionText))
#component('mail::subcopy')
If you’re having trouble clicking the "{{ $actionText }}" button, copy and paste the URL below
into your web browser: [{{ $actionUrl }}]({{ $actionUrl }})
#endcomponent
#endif
#endcomponent

Run this command
php artisan vendor:publish --tag=laravel-notifications
php artisan vendor:publish --tag=laravel-mail
update for laravel 5.7+
php artisan vendor:publish
and then you will get:
[<number>] Tag: laravel-mail
[<number>] Tag: laravel-notifications
and then just type in that number in front to publish the file for editing
and then in
/resources/views/vendor/mail/html/
you can edit all the components and customize anything you want.
For example i have edited the sentence "All rights reserved". to "All test reserved" at the bottom of that image inside this file:
/resources/views/vendor/mail/html/message.blade.php
and this is what i got:

Make sure to have the right configuration in your config/mail.php :
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
]
],

I wrote an article on how to create a notification and modify your template including the header and footer.
It includes the explanation on how the Laravel components work and how to pass your data to a new email template.
https://medium.com/#adnanxteam/how-to-customize-laravel-5-4-notification-email-templates-header-and-footer-158b1c7cc1c
The most important part is placing the following code inside your email template:
#component('mail::layout')
{{-- Header --}}
#slot('header')
#component('mail::header', ['url' => config('app.url')])
Header Title
#endcomponent
#endslot
{{-- Body --}}
This is our main message {{ $user }}
{{-- Subcopy --}}
#isset($subcopy)
#slot('subcopy')
#component('mail::subcopy')
{{ $subcopy }}
#endcomponent
#endslot
#endisset
{{-- Footer --}}
#slot('footer')
#component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. Super FOOTER!
#endcomponent
#endslot
#endcomponent
You can check the medium article in case you want more details on how the components work and how to properly pass the data.

#Brian
You can just make change to the #component directives in your template file to use your custom templates. For example:
Replace #component('mail::message') with #component('vendor.mail.html.message'), assuming your template is located at /resources/views/vendor/mail/html/message.blade.php

I ended up just using a custom view rather than trying to get the built in Laravel ones to work.
I added the following use statement to my Notification class
use Illuminate\Support\Facades\View;
use Illuminate\Support\HtmlString;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;
Then in the toMail method:
public function toMail($notifiable)
{
$view_file = 'emails.teamInvitation';
$view = View::make($view_file, ['sender' => $this->sender, 'invitationToken' => $this->invitationToken, 'team' => $this->team ]);
$view = new HtmlString(with(new CssToInlineStyles)->convert($view));
return (new MailMessage)
->subject('PreSource Invitation From ' . $this->sender->name )
->view('emails.htmlBlank', ['bodyContent' => $view]);
}
emails.teamInvitation is my actual email template.
I compile the view in to a string, and then convert the stylesheets to be inline.
emails.htmlBlank is a view file but all it does is echo out bodyContent. This is necessary because the MailMessage->view method expects a view file, and not an HtmlString.

Do NOT do what is suggested here.
This works. Just remember that you should edit the templates contained in the 'vendor/mail/html' folder AND NOT the contents of the 'vendor/mail/markdown' folder, unless of course you are using markdown instead of the line() / greeting() email building functions
Instead, run the artisan commands and then edit the generated files in your resources folder that you end up with. Never overwrite the vendor files, as if you are working on a local version, then push it to a live server and run composer install, you will not have those changes anymore.
Laravel's inheritance allows you to easily overwrite pre-defined methods and files, so take advantage of that for cleaner version control and better ability to roll back changes to core functionality.

You are making email based on component #component('mail::message') This is a default and this is only one described in documentation. This component does not allow you to modify header. However if you look into it's file,
\vendor\laravel\framework\src\Illuminate\Mail\resources\views\markdown\message.blade.php
you will see that it uses another component #component('mail::layout'),
Just copy content of message.blade.php file into your .blade.php and replace {{ $slot }} with what you had in your file before.
And now you have all the flexibility in your file.
Plus
if you want to modify styles, go to file \config\mail.php
and change markdown section like so
'markdown' => [
'theme' => 'default0',
'paths' => [
resource_path('views/vendor/mail'),
base_path('resources/views/emails/vendor'),
],
],
In this case I replaced default theme with my own \resources\views\emails\vendor\html\themes\default0.css
or, if you don't want customising paths - put your default0.css into /resources/views/vendor/mail/html/themes - it is a default path and you don't need to mention it.
Tested on Laravel 5.7

Laravel 5.8
I found email layout in file -> vendor/laravel/framework/src/Illuminate/Mail/resources/views/html/layout.blade.php.
Like I don't use markdown to send my emails, i need of layout default of laravel (yes, because i want :)).
What i did? I sent for me email for me of reset password, saved the email like html and then copied html to my editor and it ready to changes \o/.

Related

How can I forward an email in laravel?

I am using https://github.com/Webklex/laravel-imap to retrieve email from my mail server.
Now I need to forward the exact mail with all body(text, html, attachments). How can I forward the email?
I was working on the same thing, and wanted to give a more concrete answer related to using Laravel-IMAP to get emails and then forward them.
See this answer which helped out a lot.
use Illuminate\Mail\Mailable;
public function build()
{
$this->view('emails.emails.received')->with('body', $this->message->getHTMLBody())
->from($this->message->getFrom()->first()->mail, $this->message->getFrom()->first()->personal)
->to('<forwarded_email>')
->replyTo($this->message->getReplyTo()->first()->mail, $this->message->getReplyTo()->first()->personal)
->subject($this->message->getSubject());
foreach ($this->message->getAttachments() as $attachment) {
$this->attach($attachment);
}
$this->withSwiftMessage(function ($msg) {
$msg->getHeaders()->addTextHeader('In-Reply-To', $this->message->getMessageId());
$msg->getHeaders()->addTextHeader('references', $this->message->getReferences());
});
return $this;
}
Also - My template emails.emails.received contains only the following:
{!! $body !!}
Sure, that's possible, I recommend you to use:
Laravel Mail Auto Embed
Its use is very simple, you write your markdown normally:
<!-- eg: resources/vendor/mail/markdown/order-shipped.blade.php -->
#component('mail::message')
# Order Shipped
Your order has been shipped!
#component('mail::button', ['url' => $url])
View Order
#endcomponent
Purchased product:
![product](https://domain .com/products/product-1.png)
Thanks,<br>
{{ config('app.name') }}
#endcomponent
And when you send the email, it replace the url image for inline data, most simple to handle and to forward an email
When sending, it will replace the link that would normally be generated:
<img src="https://example.com/products/product-1.png">
by an embedded inline attachment of the image:
<img src="cid:3991f143cf1a86257f8671883736613c#Swift.generated">
I will give you the idea
first you need to take this email data which you have taken from laravel-imap and store in a variable
first you need to specify the wanted message lets say you are looking for a message that contains specific information which can be specified
like this
foreach($aFolder as $oFolder){
//Get all messages by custom search criteria
/** #var \Webklex\IMAP\Support\MessageCollection $aMessage */
$aMessage = $oFolder->query()->where(["CUSTOM_Word" => "Hello"]])->get();
}
now you have a specific email with all of its components
now send it to the desired email or list of emails (use foreach)
pass $aMessage variable to your send function then
$receiver_email = 'john#gmail.com';
$data = array ('subject' => '$aMessage->getSubject().'<br />'' ,
'Attachments' => '$aMessage->getAttachments()->count().'<br />'',
'body' => '$aMessage->getHTMLBody(true)';
)
Mail::send('emails.message', $data, function ($message) {
$message->to($receiver_email)
->subject($aMessage->getSubject());
$message->from('youremail#app.com' , 'your name')
});
and in your emails/message don't forget to put your custom message with subject , attachments and body as an output
in emails/message it will be the message which will be sent to the client and you can design it using html , css just like any other file it uses the laravel blade template here is an example from Medium
Hello <strong>{{ $subject}}</strong>
<p>{{$body}}</p>
Note : you might find some typos or errors because like what i have told you i gave you the idea but can't give you exactly what you want.
and here you can find another question about sending emails you might want to take a look at it
Mail::send( ['html' => 'emails.newinvoice'], ['text' => $emailtext],
// ^^^^
Also replace auto-escaped block {{ }} with unescaped {!! !!} in the template:
<p> {!! $text !!} </p>

How to customize/change the header logo of notification mail (laravel)

I have no idea where could I found the header logo example below(squared):
My content code:
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.'.$user->name)
->line('Hey Now hey now')
->action('Notification Action', route('register.select'))
->line('Thank you for using our application!');
}
EDITED: in email.blade.php:
#component('mail::message')
{{-- Greeting --}}
#if (! empty($greeting))
# {{ $greeting }}
#else
#if ($level === 'error')
# #lang('Whoops!')
#else
# #lang('Hello!')
#endif
#endif
{{-- Intro Lines --}}
#foreach ($introLines as $line)
{{ $line }}
#endforeach
... and so on, and so forth
I want to customize that logo header, my problem I can't find where exactly. Need your help Sirs.
You just have to customize mail templates.
Run this command:
php artisan vendor:publish --tag=laravel-notifications
After that you'll find templates in resources/views/vendor/notifications.
If you want to customize mail components used in templates:
php artisan vendor:publish --tag=laravel-mail
You'll find components in resources/views/vendor/mail. The ones you want are message.blade.php and header.blade.php.
It only shows the Laravel logo if the APP_NAME in the .env is set as "Laravel".
So once you change this setting it will omit the default logo. if you want to change it to display something else you can publish the mail components as other answers showed and customize it.

Laravel default auth module translation

I have generated the default Laravel auth module.
Everywhere in the blades of the module, I see Double Underscore __ function assuming that translation is almost there.
for example
<li>
<a class="nav-link" href="{{ route('login') }}">
{{ __('Login') }}
</a>
</li>
My question: Where is the translation file? Where should I put it, if I create one?
I mean, if I go to the Laravel documentation site there are examples like this
echo __('messages.welcome');
with explanations
For example, let's retrieve the welcome translation string from the resources/lang/messages.php language file:
BUT in my example above there is no file name specified. It is only text:
__('Login')
Question is: What is used for the language file if no file specified? Is there any default? Where does it sit? Where was it set?
All the language translation files in Laravel should be stored in PROJECT_DIRECTORY/resources/lang. When you make an Auth with artisan, it automatically creates it. But if you can't find it, then create manually.
(1)
There's a way to using translation strings as keys by the docs. In this method you can create a JSON file in PROJECT_DIRECTORY/resources/lang with the name of your local, for example for Spanish name it es.json or German de.json, it depends on your local name.
Now create a JSON object and put the translations with the string name you used in your blade:
{
"Login": "Welcome to Login Page!",
"Logout": "You are logged out!",
}
Then use the PHP double underscores method to call your translations in blades:
{{ __('Login') }}
(2)
Create a file named auth.php in PROJECT_DIRECTORY/resources/lang directory. then put a simple php array like this on it:
<?php
return [
/*
Translations go here...
*/
];`
Then add your translate strings to it:
<?php
return [
'Login' => 'Welcome to Login Page!',
'Logout' => 'You are logged out!',
];`
Now in the blade template simply do this:
<li>
<a class="nav-link" href="{{ route('login') }}">
{{ __('auth.Login') }}
</a>
</li>
Laravel Docs
Have an instruction about the json file. Yes it is not php, but json file. Example would be:
resources/lang/es.json
content
{
"I love programming.": "Me encanta programar."
}
Usage
echo __('I love programming.');
It looks like there are no translation file for the default __('Login'), __('Register'), ... provided by laravel.
By default if no translation is found for __('foobar'), laravel just uses the string in the parentheses. So here, assuming there is no translation file, __('Login') is expanded to 'Login'.

Not receiving the $message variable in view from a Laravel HTML Mailable (NON Markdown)

I've read several similar questions related to this problem but all refer to Markdown mailables.
I'm trying to send inline images in the mailables but I haven't found a way to do it properly (Laravel 5.5).
The documentation says this:
Inline Attachments
Embedding inline images into your emails is typically cumbersome; however, Laravel provides a convenient way to attach images to your emails and retrieving the appropriate CID. To embed an inline image, use the embed method on the $message variable within your email template. Laravel automatically makes the $message variable available to all of your email templates, so you don't need to worry about passing it in manually:
<body>
Here is an image:
<img src="{{ $message->embed($pathToFile) }}">
</body>
But, when doing that I receive this error:
Undefined variable: message (View: /path/to/project/resources/views/mails/new_user_welcome.blade.php)
I know that this has a limitation when using a Markdown message but I'm not using one.
This are the related files:
Mail/NewUserWelcomeEmail.php
class NewUserWelcomeEmail extends Mailable
{
use SerializesModels;
public function build()
{
return $this->view('mails.new_user_welcome');
}
}
Resources/views/mails/new_user_welcome.blade.php
#extends('layouts.mail')
#section('content')
<img src="{{ $message->embed(url("storage/images/inline_image.png")) }}"
alt="An inline image" />
#endsection
App/Http/Controllers/UserController.php
public function register(NewUserRequest $request)
{
// some code
Mail::to($user)->send(new NewUserWelcomeEmail($user));
return 'done';
}
Well, to be honest, I haven't found a way to make this work properly. I mean, as it stands, this should work. Maybe is my Laravel installation (?)..
Anyway, I did make it work with a workaround.
1) Using Eduardokum' Laravel Mail Auto Embed package, this basically generate a CID for each of your media assets.
But after adding this package this didn't work as expected.. so I:
2) change the way I was referencing my assets, from this:
<img src="{{ url('storage/inline_image.png') }}" />
To this:
<img src="{{ asset('storage/inline_image.png') }}" />
Now it works.
In my case (Larvel 5.5), I've managed, to modify header logo, in both html and markdown.
Laravel documentation, although really great, could be better in this regard.
Anyway, follow these steps, and you should be fine...
1 - Publish mail templates via:
php artisan vendor:publish --tag=laravel-mail
so you can easily modify your mail source files.
2 - Modify message.blade.php in resources/views/vendor/mail/html with this:
#slot('header')
#component('mail::header', ['url' => config('app.url')])
<img src="{{asset('assets/img/pathToYourImage...')}}">
#endcomponent
#endslot
3 - All your emails should receive logo via CID from now.
Note:
In this example Laravel, automatically converts assets to CIDs, so you don't need to call $message->embed(... at all...
Please test extensively, with these html/markdown directories and blade directives going on. It is kinda tricky, but it definitely, does its magic...
if you can use like this than it can be work other wise you don't use $message variable in mail blade
Mail::send('emails.welcome', $data, function ($message) {
$message->from('us#example.com', 'Laravel');
$message->to('foo#example.com')->cc('bar#example.com');
});
if you don't want use this method than you can use like this
https://code.tutsplus.com/tutorials/how-to-send-emails-in-laravel--cms-30046
it can be work like this.
You have to define the File Path Variable in your Mailable as public property -> example $pathToFile.
If you have the file path from outside of the mailable you can pass in with the constructor.
class NewUserWelcomeEmail extends Mailable
{
use SerializesModels;
// Must be public
public $pathToFile;
/**
* Create a new message instance.
*/
public function __construct(string $pathToFile)
{
$this->pathToFile= $pathToFile;
}
public function build()
{
return $this->view('mails.new_user_welcome');
}
}
Then it works as expected in your view like this:
#extends('layouts.mail')
#section('content')
<img src="{{ $message->embed(url($pathToFile)) }}" alt="An inline image" />
#endsection
This way you can embed images in markdown Laravel mails (used here to eg embed a logo):
app/Mail/Demo.php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Swift_Image;
class Demo extends Mailable
{
use Queueable, SerializesModels;
public $image_logo_cid;
/**
* Build the message.
*
* #return $this
*/
public function build()
{
// Generate a CID
$this->image_logo_cid = \Swift_DependencyContainer::getInstance()
->lookup('mime.idgenerator')
->generateId();
return $this->withSwiftMessage(function (\Swift_Message $swift) {
$image = Swift_Image::fromPath(resource_path('img/make-a-wish-logo-rev.gif'));
$swift->embed($image->setId($this->image_logo_cid));
})->subject('My awesome markdown mail with an embedded image')
->markdown('emails.demo');
}
}
resources/views/emails/demo.blade.php
#component('mail::message')
{{-- Just embedding this image in the content here --}}
<img src="cid:{{ $image_logo_cid }}">
#endcomponent
Alternatively you could embed the mail::layout component and put the image in your header:
resources/views/emails/demo.blade.php
#component('mail::layout')
{{-- Header --}}
#slot('header')
#component('mail::header', ['url' => config('app.url')])
<img src="cid:{{ $image_logo_cid }}">
#endcomponent
#endslot
{{-- Body --}}
<!-- Body here -->
{{-- Subcopy --}}
#slot('subcopy')
#component('mail::subcopy')
<!-- subcopy here -->
#endcomponent
#endslot
{{-- Footer --}}
#slot('footer')
#component('mail::footer')
<!-- footer here -->
#endcomponent
#endslot
#endcomponent
Or if you always want this header just edit the resources/views/vendor/mail/html/header.blade.php file (available after php artisan vendor:publish --tag=laravel-mail). Then of course you need to create the CID/Image with every mailable as seen in the app/Mail/Demo.php (or have a base controller for that).

Posting form in Laravel 4.1 and blade template

I'm having trouble posting forms using Laravel 4.1 with the blade template engine. The problem seems to be that the full URL including http:// is being included in the form action attribute. If I hard code the form open html manually and use a relative url, it works OK, however, when it has the full url, I am getting an exception.
routes.php
Route::any("/", 'HomeController#showWelcome');
HomeController.php
public function showWelcome()
{
echo($_SERVER['REQUEST_METHOD']);
return View::make('form');
}
Form opening tag in form.blade.php
{{ Form::open(["url" => "/","method" => "post","autocomplete" => "off"]) }}
{{ Form::label("username", "Username") }}
{{ Form::text("username", Input::old("username"), ["placeholder" => "john.smith"]) }}
{{ Form::label("password", "Password") }}
{{ Form::password("password", ["placeholder" => ""]) }}
{{ Form::submit("login") }}
{{ Form::close() }}
So if I go to my home dir / in the browser, I see the form that I have created. If I fill in the form details and click submit, I am simply taken to the same page - the request method is still GET as shown by echo($_SERVER['REQUEST_METHOD']);
I notice that the full
http://localhost/subdir/public/
url is used in the form markup. If I hardcode a form open tag in such as
<form action="/subdir/public/" method="post">
it works fine and $_SERVER['REQUEST_METHOD'] shows as post.
What am I doing wrong here?
You have created the route for the post?
example:
{{Form::open(["url"=>"/", "autocomplete"=>"off"])}} //No need to later add POST method
in Route.php
Route::post('/', 'YouController#postLogin');
you have not set up a route to handle the POST. You can do that in a couple of ways.
As pointed out above:
Route::post('/', 'HomeController#processLogin');
note that if you stick with your existing Route::any that the `Route::post needs to be before it as Laravel processes them in order (I believe).
You could also handle it in the Controller method showWelcome using:
if (Input::server("REQUEST_METHOD") == "POST") {
... stuff
}
I prefer the seperate routes method. I tend to avoid Route::any and in my login pages use a Route::get and a Route::post to handle the showing and processing of the form respectively.

Resources