*
* SMTP CODE SUCCESS: 250
- * SMTP CODE SUCCESS: 552,451,452
- * SMTP CODE SUCCESS: 500,501,502,421
+ * SMTP CODE SUCCESS: 552, 451, 452
+ * SMTP CODE SUCCESS: 500, 501, 502, 421
* @access public
* @param string $from
* @return bool
@@ -885,26 +962,26 @@ public function SendAndMail($from) {
if(!$this->connected()) {
$this->error = array(
- "error" => "Called SendAndMail() without being connected");
+ 'error' => 'Called SendAndMail() without being connected');
return false;
}
- fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
+ $this->client_send('SAML FROM:' . $from . $this->CRLF);
$rply = $this->get_lines();
- $code = substr($rply,0,3);
+ $code = substr($rply, 0, 3);
if($this->do_debug >= 2) {
- $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
');
+ $this->edebug('SMTP -> FROM SERVER:' . $rply);
}
if($code != 250) {
$this->error =
- array("error" => "SAML not accepted from server",
- "smtp_code" => $code,
- "smtp_msg" => substr($rply,4));
+ array('error' => 'SAML not accepted from server',
+ 'smtp_code' => $code,
+ 'smtp_msg' => substr($rply, 4));
if($this->do_debug >= 1) {
- $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
');
+ $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply);
}
return false;
}
@@ -925,14 +1002,27 @@ public function SendAndMail($from) {
* @return bool
*/
public function Turn() {
- $this->error = array("error" => "This method, TURN, of the SMTP ".
- "is not implemented");
+ $this->error = array('error' => 'This method, TURN, of the SMTP '.
+ 'is not implemented');
if($this->do_debug >= 1) {
- $this->edebug("SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
');
+ $this->edebug('SMTP -> NOTICE: ' . $this->error['error']);
}
return false;
}
+ /**
+ * Sends data to the server
+ * @param string $data
+ * @access public
+ * @return Integer number of bytes sent to the server or FALSE on error
+ */
+ public function client_send($data) {
+ if ($this->do_debug >= 1) {
+ $this->edebug("CLIENT -> SMTP: $data");
+ }
+ return fwrite($this->smtp_conn, $data);
+ }
+
/**
* Get the current error
* @access public
@@ -952,11 +1042,11 @@ public function getError() {
* With SMTP we can tell if we have more lines to read if the
* 4th character is '-' symbol. If it is a space then we don't
* need to read anything else.
- * @access private
+ * @access protected
* @return string
*/
- private function get_lines() {
- $data = "";
+ protected function get_lines() {
+ $data = '';
$endtime = 0;
/* If for some reason the fp is bad, don't inf loop */
if (!is_resource($this->smtp_conn)) {
@@ -967,22 +1057,22 @@ private function get_lines() {
$endtime = time() + $this->Timelimit;
}
while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
- $str = @fgets($this->smtp_conn,515);
+ $str = @fgets($this->smtp_conn, 515);
if($this->do_debug >= 4) {
- $this->edebug("SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
');
- $this->edebug("SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
');
+ $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
+ $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
}
$data .= $str;
if($this->do_debug >= 4) {
- $this->edebug("SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
');
+ $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
}
// if 4th character is a space, we are done reading, break the loop
- if(substr($str,3,1) == " ") { break; }
+ if(substr($str, 3, 1) == ' ') { break; }
// Timed-out? Log and break
$info = stream_get_meta_data($this->smtp_conn);
if ($info['timed_out']) {
if($this->do_debug >= 4) {
- $this->edebug("SMTP -> get_lines(): timed-out (" . $this->Timeout . " seconds)
");
+ $this->edebug('SMTP -> get_lines(): timed-out (' . $this->Timeout . ' seconds)');
}
break;
}
@@ -990,7 +1080,7 @@ private function get_lines() {
if ($endtime) {
if (time() > $endtime) {
if($this->do_debug >= 4) {
- $this->edebug("SMTP -> get_lines(): timelimit reached (" . $this->Timelimit . " seconds)
");
+ $this->edebug('SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' seconds)');
}
break;
}
@@ -1000,4 +1090,3 @@ private function get_lines() {
}
}
-?>
diff --git a/README.md b/README.md
index 9407371..063f617 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,21 @@
# YiiMailer
-Yii extension for sending emails with layouts using [PHPMailer](http://code.google.com/a/apache-extras.org/p/phpmailer/)
+Yii extension for sending emails with layouts using [PHPMailer](https://github.com/PHPMailer/PHPMailer)
## Features
-* Based on latest PHPMailer (version 5.2.2 bundled)
+* Based on latest PHPMailer (version 5.2.6 bundled)
* Supports Yii layouts and translation
* Supports web and console applications
-* Send full HTML emails with embeded images
+* Send full HTML emails with embedded images
* Work with views like you usually do in Yii
+* Use test mode to save emails instead of sending them (useful when you don't have mail server installed locally)
## Installation
1. Copy YiiMailer folder to protected/extensions
2. Add 'ext.YiiMailer.YiiMailer' line to your imports in main and/or console yii config
-3. Copy mail.php config file to protected/config
+3. Copy mail.php config file to protected/config or add configuration array in 'params' under the key 'YiiMailer'
4. Create email layout file mail.php in protected/views/layouts/ (default path, can be changed in config)
5. Create all the views you want to use in protected/views/mail/ (default path, can be changed in config)
6. Put all images you want to embed in emails in images/mail/ (default path, can be changed in config)
@@ -31,28 +32,73 @@ $mail = new YiiMailer();
$mail->setView('contact');
$mail->setData(array('message' => 'Message to send', 'name' => 'John Doe', 'description' => 'Contact form'));
-Layout is automatically set from config but you may override it with $mail->setLayout('layoutName')
+Layout is automatically set from config but you may override it with
+
+$mail->setLayout('layoutName');
+
-Render HTML mail and set properties:
+Set the properties:
-$mail->render();
-$mail->From = 'from@example.com';
-$mail->FromName = 'John Doe';
-$mail->Subject = 'Mail subject';
-$mail->AddAddress(Yii::app()->params['adminEmail']);
+$mail->setFrom('from@example.com', 'John Doe');
+$mail->setTo(Yii::app()->params['adminEmail']);
+$mail->setSubject('Mail subject');
You may use all PHPMailer properties you would usually use.
And finally send email(s):
-if ($mail->Send()) {
- $mail->ClearAddresses();
+if ($mail->send()) {
Yii::app()->user->setFlash('contact','Thank you for contacting us. We will respond to you as soon as possible.');
} else {
- Yii::app()->user->setFlash('error','Error while sending email: '.$mail->ErrorInfo);
+ Yii::app()->user->setFlash('error','Error while sending email: '.$mail->getError());
}
+### Sending simple messages
+
+You can send email without both the layout and view by using:
+
+$mail = new YiiMailer();
+//$mail->clearLayout();//if layout is already set in config
+$mail->setFrom('from@example.com', 'John Doe');
+$mail->setTo(Yii::app()->params['adminEmail']);
+$mail->setSubject('Mail subject');
+$mail->setBody('Simple message');
+$mail->send();
+
+
+Alternatively, you may also send email message with layout but without specific view (set layout and set body) or with view but without layout (clear layout and set view).
+
+### Setting addresses
+
+When using methods for setting addresses (setTo(), setCc(), setBcc(), setReplyTo()) any of the following is valid for arguments:
+
+$mail->setTo('john@example.com');
+$mail->setTo(array('john@example.com','jane@example.com'));
+$mail->setTo(array('john@example.com'=>'John Doe','jane@example.com'));
+
+
+### Sending attachments
+
+You may send one or more attachments using setAttachemnt() method:
+
+$mail->setAttachment('something.pdf');
+$mail->setAttachment(array('something.pdf','something_else.pdf','another.doc'));
+$mail->setAttachment(array('something.pdf'=>'Some file','something_else.pdf'=>'Another file'));
+
+
+### Test mode
+
+When working locally without mail server installed, it may be useful to save emails as files instead of trying to send them and getting errors in the process.
+To use test mode, you must specify path to directory where you want to save your emails and set 'testMode' property to 'true' in your config:
+
+
+ 'savePath' => 'webroot.assets.mail',
+ 'testMode' => true,
+
+
+Emails are saved as .eml files and you can use software like Mozilla Thunderbird to open them.
+
## Examples
Two examples included: one for standard contact form in yii web app and the other one for yii console app.
\ No newline at end of file
diff --git a/YiiMailer.php b/YiiMailer.php
index e749c23..e861135 100644
--- a/YiiMailer.php
+++ b/YiiMailer.php
@@ -2,6 +2,7 @@
/**
* YiiMailer class - wrapper for PHPMailer
* Yii extension for sending emails using views and layouts
+ * https://github.com/vernes/YiiMailer
* Copyright (c) 2013 YiiMailer
*
* This library is free software; you can redistribute it and/or
@@ -23,7 +24,7 @@
* @author Vernes Šiljegović
* @copyright Copyright (c) 2013 YiiMailer
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
- * @version 1.1, 2013-02-02
+ * @version 1.5, 2013-06-03
*/
@@ -56,6 +57,10 @@ class YiiMailer extends PHPMailer {
private $layoutPath='application.views.mail.layouts';
private $baseDirPath='webroot.images.mail';
+
+ private $testMode=false;
+
+ private $savePath='webroot.assets.mail';
private $layout;
@@ -132,6 +137,14 @@ public function getView()
return $this->view;
}
+ /**
+ * Clear currently used view
+ */
+ public function clearView()
+ {
+ $this->view=null;
+ }
+
/**
* Send data to be used in mail body
* @param array $data Data array
@@ -150,6 +163,14 @@ public function getData()
return $this->data;
}
+ /**
+ * Clear current data array
+ */
+ public function clearData()
+ {
+ $this->data=array();
+ }
+
/**
* Set layout file to be used
* @param string $layout Layout filename
@@ -174,6 +195,14 @@ public function getLayout()
return $this->layout;
}
+ /**
+ * Clear current layout
+ */
+ public function clearLayout()
+ {
+ $this->layout=null;
+ }
+
/**
* Set path for email views
* @param string $path Yii path
@@ -237,6 +266,156 @@ public function getBaseDirPath()
return $this->baseDirPath;
}
+ /**
+ * Set From address and name
+ * @param string $address Email address of the sender
+ * @param string $name Name of the sender
+ * @param boolean $auto Also set the Reply-To
+ * @return boolean True on success, false if address not valid
+ */
+ public function setFrom($address, $name = '', $auto = true)
+ {
+ return parent::SetFrom($address, $name, (int)$auto);
+ }
+
+ /**
+ * Set one or more email addresses to send to
+ * Valid arguments:
+ * $mail->setTo('john@example.com');
+ * $mail->setTo(array('john@example.com','jane@example.com'));
+ * $mail->setTo(array('john@example.com'=>'John Doe','jane@example.com'));
+ * @param mixed $addresses Email address or array of email addresses
+ * @return boolean True on success, false if addresses not valid
+ */
+ public function setTo($addresses)
+ {
+ $this->ClearAddresses();
+ return $this->setAddresses('to',$addresses);
+ }
+
+ /**
+ * Set one or more CC email addresses
+ * @param mixed $addresses Email address or array of email addresses
+ * @return boolean True on success, false if addresses not valid
+ */
+ public function setCc($addresses)
+ {
+ $this->ClearCCs();
+ return $this->setAddresses('cc',$addresses);
+ }
+
+ /**
+ * Set one or more BCC email addresses
+ * @param mixed $addresses Email address or array of email addresses
+ * @return boolean True on success, false if addresses not valid
+ */
+ public function setBcc($addresses)
+ {
+ $this->ClearBCCs();
+ return $this->setAddresses('bcc',$addresses);
+ }
+
+ /**
+ * Set one or more Reply-To email addresses
+ * @param mixed $addresses Email address or array of email addresses
+ * @return boolean True on success, false if addresses not valid
+ */
+ public function setReplyTo($addresses)
+ {
+ $this->ClearReplyTos();
+ return $this->setAddresses('Reply-To',$addresses);
+ }
+
+ /**
+ * Set one or more email addresses of different kinds
+ * @param string $type Type of the recipient (to, cc, bcc or Reply-To)
+ * @param mixed $addresses Email address or array of email addresses
+ * @return boolean True on success, false if addresses not valid
+ */
+ private function setAddresses($type,$addresses)
+ {
+ if(!is_array($addresses))
+ {
+ $addresses=(array)$addresses;
+ }
+
+ $result=true;
+ foreach ($addresses as $key => $value) {
+ if(is_int($key))
+ $r=$this->AddAnAddress($type,$value);
+ else
+ $r=$this->AddAnAddress($type,$key,$value);
+ if($result && !$r)
+ $result=false;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Set subject of the email
+ * @param string $subject Subject of the email
+ */
+ public function setSubject($subject)
+ {
+ $this->Subject=$subject;
+ }
+
+ /**
+ * Set text body of the email
+ * @param string $body Textual body of the email
+ */
+ public function setBody($body)
+ {
+ $this->Body=$body;
+ }
+
+ /**
+ * Set one or more email attachments
+ * Valid arguments:
+ * $mail->setAttachment('something.pdf');
+ * $mail->setAttachment(array('something.pdf','something_else.pdf','another.doc'));
+ * $mail->setAttachment(array('something.pdf'=>'Some file','something_else.pdf'=>'Another file'));
+ * @param mixed $attachments Path to the file or array of files to attach
+ * @return boolean True on success, false if addresses not valid
+ */
+ public function setAttachment($attachments)
+ {
+ if(!is_array($attachments))
+ $attachments=(array)$attachments;
+
+ $result=true;
+ foreach ($attachments as $key => $value) {
+ if(is_int($key))
+ $r=$this->AddAttachment($value);
+ else
+ $r=$this->AddAttachment($key,$value);
+ if($result && !$r)
+ $result=false;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Clear all recipients and attachments
+ */
+ public function clear()
+ {
+ $this->ClearAllRecipients();
+ $this->ClearReplyTos();
+ $this->ClearAttachments();
+ }
+
+ /**
+ * Get current error message
+ * @return string Error message
+ */
+ public function getError()
+ {
+ return $this->ErrorInfo;
+ }
+
/**
* Find the view file for the given view name
* @param string $viewName Name of the view
@@ -293,17 +472,20 @@ public function renderView($viewName,$viewData=null)
*/
public function render()
{
- //render body
- $body=$this->renderView($this->viewPath.'.'.$this->view, $this->data);
+ //render view as body if specified
+ if(isset($this->view))
+ $this->setBody($this->renderView($this->viewPath.'.'.$this->view, $this->data));
+
+ //render with layout if given
if($this->layout)
{
//has layout
- $this->MsgHTMLWithLayout($body, Yii::getPathOfAlias($this->baseDirPath));
+ $this->MsgHTMLWithLayout($this->Body, Yii::getPathOfAlias($this->baseDirPath));
}
else
{
//no layout
- $this->MsgHTML($body, Yii::getPathOfAlias($this->baseDirPath));
+ $this->MsgHTML($this->Body, Yii::getPathOfAlias($this->baseDirPath));
}
}
@@ -316,5 +498,60 @@ protected function MsgHTMLWithLayout($message, $basedir = '')
{
$this->MsgHTML($this->renderView($this->layoutPath.'.'.$this->layout, array('content'=>$message,'data'=>$this->data)), $basedir);
}
-
+
+ /**
+ * Render message and send emails
+ * @return boolean True if sent successfully, false otherwise
+ */
+ public function send()
+ {
+ //render message
+ $this->render();
+
+ //send the message
+ try{
+ //prepare the message
+ if(!$this->PreSend())
+ return false;
+
+ //in test mode, save message as a file
+ if($this->testMode)
+ return $this->save();
+ else
+ return $this->PostSend();
+ } catch (phpmailerException $e) {
+ $this->mailHeader = '';
+ $this->SetError($e->getMessage());
+ if ($this->exceptions) {
+ throw $e;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Save message as eml file
+ * @return boolean True if saved successfully, false otherwise
+ */
+ public function save()
+ {
+ $filename = date('YmdHis') . '_' . uniqid() . '.eml';
+ $dir = Yii::getPathOfAlias($this->savePath);
+ //check if dir exists and is writable
+ if(!is_writable($dir))
+ throw new CException('Directory "'.$dir.'" does not exist or is not writable!');
+
+ try {
+ $file = fopen($dir . DIRECTORY_SEPARATOR . $filename,'w+');
+ fwrite($file, $this->GetSentMIMEMessage());
+ fclose($file);
+
+ return true;
+ } catch(Exception $e) {
+ $this->SetError($e->getMessage());
+
+ return false;
+ }
+ }
+
}
\ No newline at end of file
diff --git a/example/files/yii-1.1.0-validator-cheatsheet.pdf b/example/files/yii-1.1.0-validator-cheatsheet.pdf
new file mode 100644
index 0000000..ec156d7
Binary files /dev/null and b/example/files/yii-1.1.0-validator-cheatsheet.pdf differ
diff --git a/example/protected/commands/CronCommand.php b/example/protected/commands/CronCommand.php
index f2c2e2c..ff8bc0d 100644
--- a/example/protected/commands/CronCommand.php
+++ b/example/protected/commands/CronCommand.php
@@ -11,23 +11,21 @@ public function run($args)
//Do some cron processing...
$cronResult="Cron job finished successfuly";
- $mail = new YiiMailer();
+ $mail = new YiiMailer;
//use "cron" view from views/mail
$mail->setView('cron');
$mail->setData(array('message' => $cronResult, 'name' => get_class($this), 'description' => 'Cron job', 'mailer' => $mail));
- //render HTML mail, layout is set from config file or with $mail->setLayout('layoutName')
- $mail->render();
- //set properties as usually with PHPMailer
- $mail->From = 'from@example.com';
- $mail->FromName = 'Console application';
- $mail->Subject = $cronResult;
- $mail->AddAddress('to@example.com');
+
+ //set properties
+ $mail->setFrom('from@example.com', 'Console application');
+ $mail->setSubject($cronResult);
+ $mail->setTo('to@example.com');
+ $mail->setAttachment(Yii::getPathOfAlias('webroot.files') . '/yii-1.1.0-validator-cheatsheet.pdf');
//send
- if ($mail->Send()) {
- $mail->ClearAddresses();
+ if ($mail->send()) {
echo 'Mail sent successfuly';
} else {
- echo 'Error while sending email: '.$mail->ErrorInfo;
+ echo 'Error while sending email: '.$mail->getError();
}
echo PHP_EOL;
}
diff --git a/example/protected/config/mail.php b/example/protected/config/mail.php
index ef47de2..27aa602 100644
--- a/example/protected/config/mail.php
+++ b/example/protected/config/mail.php
@@ -3,6 +3,8 @@
'viewPath' => 'application.views.mail',
'layoutPath' => 'application.views.layouts',
'baseDirPath' => 'webroot.images.mail',
+ 'savePath' => 'webroot.assets.mail',
+ 'testMode' => false,
'layout' => 'mail',
'CharSet' => 'UTF-8',
'AltBody' => Yii::t('YiiMailer','You need an HTML capable viewer to read this message.'),
diff --git a/example/protected/controllers/SiteController.php b/example/protected/controllers/SiteController.php
index 1849efa..f337fa3 100644
--- a/example/protected/controllers/SiteController.php
+++ b/example/protected/controllers/SiteController.php
@@ -59,19 +59,16 @@ public function actionContact()
{
//use 'contact' view from views/mail
$mail = new YiiMailer('contact', array('message' => $model->body, 'name' => $model->name, 'description' => 'Contact form'));
- //render HTML mail, layout is set from config file or with $mail->setLayout('layoutName')
- $mail->render();
- //set properties as usually with PHPMailer
- $mail->From = $model->email;
- $mail->FromName = $model->name;
- $mail->Subject = $model->subject;
- $mail->AddAddress(Yii::app()->params['adminEmail']);
+
+ //set properties
+ $mail->setFrom($model->email, $model->name);
+ $mail->setSubject($model->subject);
+ $mail->setTo(Yii::app()->params['adminEmail']);
//send
- if ($mail->Send()) {
- $mail->ClearAddresses();
+ if ($mail->send()) {
Yii::app()->user->setFlash('contact','Thank you for contacting us. We will respond to you as soon as possible.');
} else {
- Yii::app()->user->setFlash('error','Error while sending email: '.$mail->ErrorInfo);
+ Yii::app()->user->setFlash('error','Error while sending email: '.$mail->getError());
}
$this->refresh();