during the conception of my web site using laravel 7 I found some troubles when I wanted to customize my laravel customize my Laravel email verification message.
first, I went to this file "C:\xampp\htdocs\clinique_juridique\vendor\laravel\framework\src\Illuminate\Auth\Notifications\VerifyEmail.php",then i translate the sentences that are there .
Here I met two issues, the first one some sentences are not in VerifyEmail.php so I couldn't translate them the second one, I didn't know how to change the style of the message, of course, also I didn't know how to include the logo of my website.
thank you in advance.
So I think you're asking about this specific part of the code (took from Laravel 8 vendor code):
/**
* Build the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return (new MailMessage)
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $verificationUrl)
->line(Lang::get('If you did not create an account, no further action is required.'));
}
First, you may use this class VerifyEmail and method toMail to send this confirmation email to your client, but if you look closer, you can identify that this function is using the Lang class, which can be found at Laravel Localization.
Otherwise, if you want to make it more complex you can create your own class MyVerifyEmail which extends VerifyEmail and recreate toMail method to serve your needs, but I can't think of a scenario where you do this.
Second, if you want to include some personalized style of your mail, try creating a Mailable, with this approach you can use a custom view (of course including a logo).
And finally, I encourage to do not to modify the vendor's directory, because it stores all the libraries that laravel needs to work properly. Instead, extend any class which you may want to modify.
Related
I want to fetch all emails in inbox and store them in realtime but the way i m doing it is a little bit wrong :
it's something like this:
class Kernel extends ConsoleKernel
...
$schedule->call(function () {
// connect using credentials
//get all emails
//copy emails
//delete emails when they get copied correctly
})->everyMinute(); // repeat
this Guarantees that the emails in the database will not be copied because the emails are no longer in the inbox .
But now we have a case where we need to keep these messages so we replaced this with :
// connect using credentials
//get all emails
//filter only unseen
//copy emails
//mark these emails as seen
But the problem of all of these solution is that we get emails .
The solution we did is working but added another problem of reloading same emails over and over .
I reread the Documentation and found this
possible solution :
Using events that triggers in the entire package and capture the new event witch gets triggered when a new email is received.
In this example i found some useful methods and classes .
class CustomMessageNewEvent extends Webklex\PHPIMAP\Events\MessageNewEvent {
/**
* Create a new event instance.
* #var \Webklex\PHPIMAP\Message[] $messages
* #return void
*/
public function __construct($messages) {
$this->message = $messages[0];
echo "New message: ".$this->message->subject."\n";
}
}
But i m not sure how to implement them in Laravel , juste where and how should i register / capture this `new` Event !
**Especially that we have multiple Client instances not only one ( foreach user ... )**
Thank you so much for reading all this and hopefully people find this question useful.
When I've done similar tasks in the past I've used the Message-ID defined in the email spec to identify each email uniquely.
You can use your IMAP library to get a list of emails with their Message-IDs, then compare those Message-IDs to your database. Just make sure you add a new field to your Email model (or whatever you've called it) to store the Message-ID.
I want to get the render of an email before to send it.
I created a TemplatedEmail with htmlTemplate and context, it works fine for sending but how get the generated template with context to save it in database ? (customer needs)
I tried the getBody() but seems to work only with text template as I get A message must have a text or an HTML part or attachments.
$email = new TemplatedEmail();
$email->htmlTemplate($htmlTemplate);
$email->from($from)->to(...$to)->subject($subject);
$email->context($context);
dd($email->getBody());
I thought to use the render method but I'm in a service and not sure if it's a good way to store in database.
Symfony only renders the message when actually sending it, via an Event Listener. The class responsible from doing the rendering is BodyRenderer, from the Twig Bridge.
But nothing stops you from rendering the message yourself.
You have the template and the context variables, so you could simply inject Twig wherever you are doing the sending, render the template to a string and do whatever you need with that.
You could also register your own MessageEvent::class listener, set it with lower priority than the one registered by the Twig Bundle (it uses the default priority) so it's executed after that one, and then you could access the message body since it would have been rendered already. This is (very) slightly more complex, but you'd gain some performance since you wouldn't be rendering the template twice.
Which approach to use would depend on your application, your constraints, etc. But the important bit is to realize on what part of the process you'll find the body actually rendered, or render it yourself if you want it before that.
For future information it's possible to render the template in a service using the documentation here https://symfony.com/doc/current/templates.html#rendering-a-template-in-services
Here is solution (for logging templated email to DB, but can be easily customized to anything else) using EventListener
use App\Entity\EmailLog;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
#[AsEventListener(event: MessageEvent::class, method: 'onEmailSent', priority: -1)]
class EmailLogListener
{
public function __construct(
private EntityManagerInterface $entityManager
) {}
public function onEmailSent(MessageEvent $event): void
{
$message = $event->getMessage();
if (!$message instanceof Email) {
return;
}
$log = new EmailLog();
$log
->setSentFrom(array_map(function (Address $address) {
return $address->getAddress();
}, $message->getFrom()))
->setSentTo(array_map(function (Address $address) {
return $address->getAddress();
}, $message->getTo()))
->setSubject($message->getSubject())
->setText($message->getHtmlBody());
$this->entityManager->persist($log);
$this->entityManager->flush();
}
}
I am writing a plug-on that should add an (dynamic) attachment to the email that is send to the end user. But I am stuck on one thing.
Firstly I was using the EMAIL_ON_SEND hook to add an attachment to the email. But it seems that it will add a attachment to each email everytime it is called.
For each email it is called two times. So to the first mail it will add 2 attachments and for the second one 4, etc etc.
The second approach was to use the ON_SENT_EMAIL_TO_USER hook. But this one does not seems to be called before the email (in a segment) is send.
class EmailSubscriber extends CommonSubscriber
{
protected $helper;
public function __construct(IntegrationHelper $helper)
{
$this->helper = $helper;
$this->parser = new ApiParser();
}
/**
* #return array
*/
public static function getSubscribedEvents()
{
return [
// EmailEvents::EMAIL_ON_SEND => ['onEmailSend', 100],
EmailEvents::ON_SENT_EMAIL_TO_USER => ['onEmailSend', 100],
];
}
/**
* Search and replace tokens with content
*
* #param EmailSendEvent $event
*/
public function onEmailSend(EmailSendEvent $event)
{
error_log('123');
}
Someway I have to hook on the actual action that is sending the email instead of the event (?). But I can't figure out which one
I can't answer directly but might be able to point you at some useful resources!
Firstly, are you trying to send the email to the Mautic user (e.g. the administrator or the owner of the lead), or to the lead? Just wanted to double check we're looking at the right thing as they are often confused!
It also depends on what you're trying to do, attach a file which isn't currently part of Mautic (e.g. an invoice or something like that) or if you're trying to attach a file which you want to track in Mautic as an asset.
In terms of attachments, these resources from the developer documentation may be useful:
Mailhelper - https://developer.mautic.org/#mail-helper
Attachments - https://developer.mautic.org/#attachments
It references attachFile() but there is also attachAsset() which allows you to attach a Mautic asset you have already uploaded (\Mautic\AssetBundle\Entity\Asset).
You may also want to take a look at https://forums.mautic.org where there may be more developers from the community able to give some further insight!
Hey Firstly thank you for the response.
The hook is called multiple times so I needed to tweak it.
So we track where it is called and filter it.
Besides that we need to clean the attachments every time.
Anyway even if it is not that clean, it does the trick
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
if (strpos($trace[4]['file'], 'SendEmailToContact.php') !== false) {
$helper = $event->getHelper();
$messageChildren = $helper->message->getChildren();
if (count($messageChildren) > 0) {
$helper->message->detach($messageChildren[0]);
}
I have some html emails that may be a bit too custom for a markdown, but I want to add the plain text versions to them. I know markdown emails do both, but can I manually add a plain text version to a mailable?
From the Laravel documentation:
If you would like to define a plain-text version of your email, you
may use the text method. Like the view method, the text method accepts
a template name which will be used to render the contents of the
email. You are free to define both a HTML and plain-text version of
your message:
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('emails.orders.shipped')
->text('emails.orders.shipped_plain');
}
I need to edit template which used for emails in admin panel. Any Ideas?
I think about several ways:
Save email template in DB in the text field, edit it in the admin panel and then display the text in the blade's view.
The problem of this way's realization is I have to display php variables in blade template and then use the final code as the html for email. I think, it's so difficult for Laravel.
And the additional problem is if I store {{ $var }} in template's text in DB - it will display as the text, blade compiler doesn't process it.
Store only the static text information from email in the DB and then display it in the template. PHP variables will transfer separately.
This way will solve the problem with the php var's display, but I still don't know how to use the final code in the Mail::send, because Laravel allows using the template's name only, not a HTML, as I know...
I think about the following way:
$view = view('template')->render();
mail(..., $view, ...);
But I don't want to use it because I want use Mail::queue() for querying emails and I don't know how to use it with PHP mail().
Thanks to everybody for replies.
You can create your own variable syntax and store email template as text in your DB. Foe example, you can store each variable as ${VARIABLE_KEY} string.
Then during email preparation you should resolve all such constructions into their real values. I don't know which variables are required, but during email preparation you should execute these steps:
Load email template from DB.
Replace all ${VARIABLE_KEY} with their real values.
You can use regular expressions for the searching and replacement, but also you can use functions such str_replace. For example, if you want to paste email of the current user into your email (and your table for model User has an email field), then you can create variable: ${user.name} and then replace this manually with simple str_replace function:
$variables['${user.name}'] = Auth::user()->email;
str_replace(array_keys($variables), array_values($variables), $yourEmailTemplateBody);
Also you can do replacements by the same method not only in the email template body, but in the email subject too.
Then you have to create your own class which extends Laravel Illuminate\Mail\Mailable class. In this class you should define build method, where you can use not only the name of the view, but also some additional parameters, like in the "regular" view, for example:
class SomeClassName extends Mailable
{
public function build()
{
$email = $this->view('mail.common', [
'mail_header' => 'some header',
'mail_footer' => 'some footer',
])->subject('Your subject');
...
return $email;
}
For example, in your view you can store layout for entire email with some extra parameters: footer and header as in my example.
Also you can create more complex syntax for ${VARIABLE_NAME} constructions, for example, VARIABLE_NAME can be a method definition in PHP or Laravel notation, i.e.: SomeClass::someStaticMethod. You can detect this case and resolve SomeClass via Laravel Service Container. Also it can be an object.field notation, for example, user.email, where user is the current Auth::user().
But be carefull in this cases: if you will grant ability to edit email templates with this variables for all users, you should filter fields or available methods and classes for calling to prevent executing any method of any available class in your email template or to prevent display private information.
You can read about writing mailables in Laravel documentation
I was doing this for a project yesterday and found a good post describing Alexander's answer in more detail. The core is creating an EmailTemplate model with this method:
public function parse($data)
{
$parsed = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($data) {
list($shortCode, $index) = $matches;
if( isset($data[$index]) ) {
return $data[$index];
} else {
throw new Exception("Shortcode {$shortCode} not found in template id {$this->id}", 1);
}
}, $this->content);
return $parsed;
}
Example usage:
$template = EmailTemplate::where('name', 'welcome-email')->first();
Mail::send([], [], function($message) use ($template, $user)
{
$data = [
'firstname' => $user->firstname
];
$message->to($user->email, $user->fullname)
->subject($template->subject)
->setBody($template->parse($data));
});
For all the details (db migration, unit test, etc), see the original post at http://tnt.studio/blog/email-templates-from-database
You can simply use this awesome laravel package:
https://github.com/Qoraiche/laravel-mail-editor
Features (from readme file):
Create mailables without using command line.
Preview/Edit all your mailables at a single place.
Templates (more than 20+ ready to use email templates).
WYSIWYG Email HTML/Markdown editor.