I'm using the laravel-imap package to read my PEC emails from a management system.
For the uninitiated, the PEC is the Certified Electronic Mail, "Posta Elettronica Certificata, (PEC)", it is the system that allows you to send e-mails with legal value equivalent to a registered letter with return receipt.
I can read the Subject thanks to the getSubject() method, I can count the number of attachments but I can't read the body of the email.
I also tried to use the getHTMLBody(), getTextBody() methods but I get nothing, i.e. I get a blank string.
I tried doing a
dd($message->getHTMLBody(true));
but I get null.
This is my code:
public function mail()
{
$oClient = Client::account('default');
$oClient->connect();
$folders = $oClient->getFolders();
/** #var \Webklex\PHPIMAP\Folder $folder */
foreach ($folders as $folder) {
/** #var \Webklex\PHPIMAP\Support\MessageCollection $messages */
//$messages = $folder->messages()->all()->get();
$messages = $folder->query()->since(now()->subDays(30))->get();
}
foreach($messages as $message){
echo $message->getSubject().'<br />';
echo 'Attachments: '.$message->getAttachments()->count().'<br />';
echo $message->getHTMLBody(true);
}
}
Can anyone help me please? I'm desperate.
Related
I am using Mailgun as a mail driver in my laravel application, as well as nexmo for SMS purposes.
What I am trying to achieve is to maintain the delivery status of the notifications that are sent either via Mailgun or Nexmo. Incase of Nexmo I am able achieve this, since I get the nexmo MessageId in the NotificationSent event that is fired after processing a notification.
However in the event instance for email, the response is empty.
Any idea what I am missing or, how I can retrieve the mailgun message-id?
I have found a workaround that does the job for now. Not as neat as I want it to be, but posting for future references incase anyone needs this.
I have created a custom notification channel extending Illuminate\Notifications\Channels\MailChannel
class EmailChannel extends MailChannel
{
/**
* Send the given notification.
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
* #return void
*/
public function send($notifiable, Notification $notification)
{
if (! $notifiable->routeNotificationFor('mail')) {
return;
}
$message = $notification->toMail($notifiable);
if ($message instanceof Mailable) {
return $message->send($this->mailer);
}
$this->mailer->send($message->view, $message->data(), function ($m) use ($notifiable, $notification, $message) {
$recipients = empty($message->to) ? $notifiable->routeNotificationFor('mail') : $message->to;
if (! empty($message->from)) {
$m->from($message->from[0], isset($message->from[1]) ? $message->from[1] : null);
}
if (is_array($recipients)) {
$m->bcc($recipients);
} else {
$m->to($recipients);
}
if ($message->cc) {
$m->cc($message->cc);
}
if (! empty($message->replyTo)) {
$m->replyTo($message->replyTo[0], isset($message->replyTo[1]) ? $message->replyTo[1] : null);
}
$m->subject($message->subject ?: Str::title(
Str::snake(class_basename($notification), ' ')
));
foreach ($message->attachments as $attachment) {
$m->attach($attachment['file'], $attachment['options']);
}
foreach ($message->rawAttachments as $attachment) {
$m->attachData($attachment['data'], $attachment['name'], $attachment['options']);
}
if (! is_null($message->priority)) {
$m->setPriority($message->priority);
}
$message = $notification->getMessage(); // I have this method in my notification class which returns an eloquent model
$message->email_id = $m->getSwiftMessage()->getId();
$message->save();
});
}
}
I am still looking for a solution to achieve this with NotificationSent event.
When looking at the code (MailgunTransport) it will do the following
$this->client->post($this->url, $this->payload($message, $to));
$this->sendPerformed($message);
return $this->numberOfRecipients($message);
Since the Laravel contract requires the implementation to send back the number of e-mails send.
Even if you would be able to get into the mail transport it doesn't store the response from for this reason it not possible to catch the message id.
What you could do is to implement your own (or look in packagist) to adapt mail client but this is not a perfect solution and will require some ugly instanceof checks.
Working with a project that uses OAuth user registration through GitHub. Everything is working fine until the last step of confirming the account through my application.
Here is the function in question:
/**
* Get the primary, verified email address from the Github data.
*
* #param mixed $emails
* #return mixed
*/
protected function getPrimaryEmail($emails)
{
foreach ($emails as $email) {
if (! $email->primary) {
continue;
}
if ($email->verified) {
return $email->email;
}
throw new GithubEmailNotVerifiedException;
}
return null;
}
Anyone else ever experience this when working with OAuth and GitHub? Thank you
Not to be an arse, but it sounds like emails is not an array. Is it possibly a null?
Log the actual value with Log::debug('WTF IS THIS THEN?!!: '.print_r($emails, true)); and see.
I need to email all my users a daily product list in cakePHP 2.
I have the following code to get all the users emails.
$users = $this->User->find('all', array('fields' => array('email')));
foreach ($users as $user) {
$this->Email->reset();
$this->Email->from = '<no-reply#test.com.au>';
$this->Email->to = $user['email'];
$this->Email->subject = "Daily Products" ;
$this->Email->sendAs = 'html';
$this->Email->send();
}
Now I understand that I could use a html template for this and parse values to it but I really need a foreach loop inside the actual view itself and send the table of products.
What would be the best practise? cakePHP code in the controller or view to get the products?
Thanks
The best practice for this would be to use a shell to send the emails out. To avoid running out of memory you should read the users and their products in chunks and not all at the same time.
Inside the foreach loop you'll need to get the data for each user and set() it as any other variable it will be available in the html template then and you can render all the products there.
Here is some (shortened) code from a shell that processes data:
public function main() {
$this->loop();
}
public function loop() {
try {
while (true) {
if (!$this->poll()) {
$this->out(__('Nothing more to process, sleeping...'));
sleep($this->sleep);
}
}
} catch (Exception $e) {
$this->out($e->getMessage());
$this->log($e->getMessage(), 'processing');
}
}
public function poll() {
$this->out('Polling...');
$result = $this->Model->getFirstUnprocessed();
if ($result === false) {
return false;
}
// do something with the result here
return true;
}
This should be enough to give you an idea. For reading your users in chunks you would need to increment the offset in your find() options. In my case I just check if there is an unprocessed record and if yes i process and and wait a moment to do the next try.
The "view" for e-mails is actually an element. It's under Views/Elements/email. There are 2 folders there html and text, both meant to hold their respective templates.
You can do your foreach in there, then make sure to set the layout in your controller with:
$this->Email->sendAs = 'html'; // Can also be 'text' or 'both' (for multipart).
$this->Email->layout = 'foo'; // Would include Views/Elements/email/html/foo.ctp
Although the Email component is deprecated since CakePHP 2.0 and you should be using the CakeEmail component instead. See the book for more details on how to use that.
I have a function that sends out site emails (using phpmailer), what I want to do is basically for php to replace all the placheholders in the email.tpl file with content that I feed it. The problem for me is I don't want to be repeating code hence why I created a function (below).
Without a php function I would do the following in a script
// email template file
$email_template = "email.tpl";
// Get contact form template from file
$message = file_get_contents($email_template);
// Replace place holders in email template
$message = str_replace("[{USERNAME}]", $username, $message);
$message = str_replace("[{EMAIL}]", $email, $message);
Now I know how to do the rest but I am stuck on the str_replace(), as shown above I have multiple str_replace() functions to replace the placeholders in the email template. What I would like is to add the str_replace() to my function (below) and get it to find all instances of [\] in the email template I give it and replace it with the placeholders values that I will give it like this: str_replace("[\]", 'replace_with', $email_body)
The problem is I don't know how I would pass multiple placeholders and their replacement values into my function and get the str_replace("[{\}]", 'replace_with', $email_body) to process all the placeholders I give it and replace with there corresponding values.
Because I want to use the function in multiple places and to avoid duplicating code, on some scripts I may pass the function 5 placeholders and there values and another script may need to pass 10 placeholders and there values to the function to use in email template.
I'm not sure if I will need to use an an array on the script(s) that will use the function and a for loop in the function perhaps to get my php function to take in xx placeholders and xx values from a script and to loop through the placeholders and replace them with there values.
Here's my function that I referred to above. I commented the script which may explain much easier.
// WILL NEED TO PASS PERHAPS AN ARRAY OF MY PLACEHOLDERS AND THERE VALUES FROM x SCRIPT
// INTO THE FUNCTION ?
function phpmailer($to_email, $email_subject, $email_body, $email_tpl) {
// include php mailer class
require_once("class.phpmailer.php");
// send to email (receipent)
global $to_email;
// add the body for mail
global $email_subject;
// email message body
global $email_body;
// email template
global $email_tpl;
// get email template
$message = file_get_contents($email_tpl);
// replace email template placeholders with content from x script
// FIND ALL INSTANCES OF [{}] IN EMAIL TEMPLATE THAT I FEED THE FUNCTION
// WITH AND REPLACE IT WITH THERE CORRESPOING VALUES.
// NOT SURE IF I NEED A FOR LOOP HERE PERHAPS TO LOOP THROUGH ALL
// PLACEHOLDERS I FEED THE FUNCTION WITH AND REPLACE WITH THERE CORRESPONDING VALUES
$email_body = str_replace("[{\}]", 'replace', $email_body);
// create object of PHPMailer
$mail = new PHPMailer();
// inform class to use smtp
$mail->IsSMTP();
// enable smtp authentication
$mail->SMTPAuth = SMTP_AUTH;
// host of the smtp server
$mail->Host = SMTP_HOST;
// port of the smtp server
$mail->Port = SMTP_PORT;
// smtp user name
$mail->Username = SMTP_USER;
// smtp user password
$mail->Password = SMTP_PASS;
// mail charset
$mail->CharSet = MAIL_CHARSET;
// set from email address
$mail->SetFrom(FROM_EMAIL);
// to address
$mail->AddAddress($to_email);
// email subject
$mail->Subject = $email_subject;
// html message body
$mail->MsgHTML($email_body);
// plain text message body (no html)
$mail->AltBody(strip_tags($email_body));
// finally send the mail
if(!$mail->Send()) {
echo "Mailer Error: " . $mail->ErrorInfo;
} else {
echo "Message sent Successfully!";
}
}
Simple, see strtrĀDocs:
$vars = array(
"[{USERNAME}]" => $username,
"[{EMAIL}]" => $email,
);
$message = strtr($message, $vars);
Add as many (or as less) replacement-pairs as you like. But I suggest, you process the template before you call the phpmailer function, so things are kept apart: templating and mail sending:
class MessageTemplateFile
{
/**
* #var string
*/
private $file;
/**
* #var string[] varname => string value
*/
private $vars;
public function __construct($file, array $vars = array())
{
$this->file = (string)$file;
$this->setVars($vars);
}
public function setVars(array $vars)
{
$this->vars = $vars;
}
public function getTemplateText()
{
return file_get_contents($this->file);
}
public function __toString()
{
return strtr($this->getTemplateText(), $this->getReplacementPairs());
}
private function getReplacementPairs()
{
$pairs = array();
foreach ($this->vars as $name => $value)
{
$key = sprintf('[{%s}]', strtoupper($name));
$pairs[$key] = (string)$value;
}
return $pairs;
}
}
Usage can be greatly simplified then, and you can pass the whole template to any function that needs string input.
$vars = compact('username', 'message');
$message = new MessageTemplateFile('email.tpl', $vars);
The PHP solutions can be:
usage of simple %placeholder% replacement mechanisms:
str_replace,
strtr,
preg_replace
use of pure PHP templating and conditional logic (short open tags in PHP and alternative syntax for control structures)
Please, find the wide answer at Programmers.StackExchange to find out other approaches on PHP email templating.
Why dont you just make the email template a php file aswell? Then you can do something like:
Hello <?=$name?>, my name is <?=$your_name?>, today is <?=$date?>
inside the email to generate your HTML, then send the result as an email.
Seems to me like your going about it the hard way?
when i send email i get two emails but it should send email to respective emails.
Lopoping problem ?
$array_values = Array
(
[0] => Array
(
[0] => uname1
[1] => fullname1
[2] => email 1
)
[1] => Array
(
[0] => uname2
[1] => fullname2
[2] => email 2
)
)
$f=0;
foreach($array_values as $mail_vars)
{
//$mail->AddReplyTo($mail_vars[2],'RED');
$mail->AddAddress($mail_vars[2], 'sss');
$body .="<br>";
$body .= 'Username: '. $mail_vars[0];
$body .="<br>";
$body .= 'Password: '.$mail_vars[1];
$body .="<br>";
$mail->SetFrom('email', 'FULLNAME');
$mail->Subject = "NEW";
$mail->MsgHTML($body);
//$mail->Send();
$f++;
}
Looking through the source of PHP Mailer, you will need to clear the fields. At least the address, maybe more. Here is the section of code from the PHPMailer class that has the clear functions. You are more than welcomed to look through them and try them etc. This is obviously an alternative to re-instantiating a new object, which may or may not cause a memory leak (depending on how many calls you make to it).
So implementing the clearAddresses code:
$mail->Subject = "NEW";
$mail->MsgHTML($body);
$mail->Send();
$mail->ClearAddresses(); // should reset the To address and remove the first one from it.
I removed the actual code as you just need the description and function name.
/////////////////////////////////////////////////
// CLASS METHODS, MESSAGE RESET
/////////////////////////////////////////////////
/**
* Clears all recipients assigned in the TO array. Returns void.
* #return void
*/
public function ClearAddresses() {
}
/**
* Clears all recipients assigned in the CC array. Returns void.
* #return void
*/
public function ClearCCs() {
}
/**
* Clears all recipients assigned in the BCC array. Returns void.
* #return void
*/
public function ClearBCCs() {
}
/**
* Clears all recipients assigned in the ReplyTo array. Returns void.
* #return void
*/
public function ClearReplyTos() {
}
/**
* Clears all recipients assigned in the TO, CC and BCC
* array. Returns void.
* #return void
*/
public function ClearAllRecipients() {
}
/**
* Clears all previously set filesystem, string, and binary
* attachments. Returns void.
* #return void
*/
public function ClearAttachments() {
}
/**
* Clears all custom headers. Returns void.
* #return void
*/
public function ClearCustomHeaders() {
}
if you look through the php mailer code, there is this useful method ClearAllRecipients() if you want to clear to, cc, and bcc all at once.
You need a:
$mail=new PHPMailer()
in the beginning of your for loop -as it is, the second time through it just messes around with the first email (since a new one isn't created).
As you pointed out body also needs to be reset - in fact using a separated var like that isn't very helpful - better to just supply directly to MsgHTML. Since the content of your email is trivial you may also want to send a plain-text version of the data (depends on your target recipient I guess).
So the updated script:
foreach($array_values as $mail_vars)
{
$mail=new PHPMailer();
$mail->SetFrom('email', 'FULLNAME');
$mail->AddAddress($mail_vars[2], 'sss');
$mail->Subject = "NEW";
$mail->MsgHTML("<br>\nUsername: ".$mail_vars[0]."<br>\nPassword: ".$mail_vars[1]."<br>");
//$mail->Send();
$f++;
}