Strange then, on some free time will look into it.
Anyway, I have some results to share with you.
First of all, I really like the functionality provided with your module, it gave me even more then I even was thinking about. I think my client may like the idea of Mailing Lists as well
.
Second, I found 2 bugs so far, both are db related:
-
sm_queue misses headers
field, which is used in the code.
-
some indexes are wrongly set to be unique, these ones should be fixed to :
$this->createIndex('idx_sm_queue_to', 'sm_queue', 'to', false);
$this->createIndex('idx_sm_queue_subject', 'sm_queue', 'subject', false);
$this->createIndex('idx_sm_queue_status', 'sm_queue', 'status', false);
Third, I updated the code a bit as
a. I want/need to support variables in all template attributes.
b. I want/need to support global, application specific variables.
I added
public $globalReplacements = array();
to SimpleMailerModule
and assigned vars to it in init(), e.g.
public function init() {
$this->setImport(array(
'mailer.models.*',
'mailer.components.*',
));
$this->globalReplacements = array(
'__appname__'=>Yii::app()->name
);
}
Updated compileMultipartTemplate():
/**
* Heavily based on http://stackoverflow.com/questions/1606588/how-to-attach-and-show-image-in-mail-using-php and
* http://www.phpeveryday.com/articles/PHP-Email-Using-Embedded-Images-in-HTML-Email-P113.html
* @param MailerTemplate $template the template instance
* @param array $template_partials HTML code to be inserted in the email. array('__key_for_partial__' => '<h1>html</h1>').
* Defaults to array();
* @return array Contents: array($template, $headers)
* @throws CException
*/
public static function compileMultipartTemplate($template, Array $template_partials = array(), Array $template_vars = array()) {
$attach_images = Yii::app()->getModule('admin')->getModule('mailer')->attachImages;
if (!is_a($template, 'MailerTemplate')) {
throw new CException('Mailer::compileMultipartTemplate(): '. Yii::t(
'mailer',
'Wrong object passed, expected MailerTemplate instance.'
));
}
if (is_array($template_partials) && !empty($template_partials))
$template->body = strtr($template->body, $template_partials);
//Then substitute the template variables with actual data
$template_vars = array_merge($template_vars, Yii::app()->getModule('admin')->getModule('mailer')->globalReplacements);
if (!empty($template_vars)){
$template->body = strtr($template->body, $template_vars);
$template->alternative_body = strtr($template->alternative_body, $template_vars);
$template->subject = strtr($template->subject, $template_vars);
$template->from = strtr($template->from, $template_vars);
}
$boundary = md5(uniqid(time()));
// Substitute every url for img tags with a cid in $template->body
if ($attach_images) {
$paths = array();
preg_match_all('~<img.*?src=.([\/.a-z0-9:_-]+).*?>~si', $template->body, $matches);
foreach ($matches[1] as $img) {
if (strpos($img, "http://") == false) {
$content_id = md5($img);
$url = parse_url($img);
$paths[] = array(
'path' => $_SERVER['DOCUMENT_ROOT'] . $url['path'],
'cid' => $content_id,
);
$template->body = str_replace($img, 'cid:' . $content_id, $template->body);
}
}
}
// Multipart header
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/alternative; boundary=\"PHP-alt-$boundary\"\r\n";
$headers .= "From: " . $template->from . "\r\n";
$headers .= "X-Sender-IP: $_SERVER[SERVER_ADDR]\r\n";
$headers .= 'Date: ' . date('n/d/Y g:i A') . "\r\n";
// Text-only body
$multipart = "--PHP-alt-$boundary\n";
$multipart .= "Content-Type: text/plain; charset=utf-8\n";
$multipart .= "$template->alternative_body\n\n";
$multipart .= "--PHP-alt-$boundary\n";
$multipart .= "Content-Type: multipart/related; boundary=\"PHP-related-$boundary\"\n\n";
// HTML body
$multipart .= "--PHP-related-$boundary\n";
$multipart .= "Content-Type: text/html; charset=utf-8\n";
$multipart .= "Content-Transfer-Encoding: Quot-Printed\n\n";
$multipart .= "$template->body\n\n";
// Images as attachment
if ($attach_images) {
foreach ($paths as $path) {
if (file_exists($path['path']))
$fp = fopen($path['path'], "r");
if (!$fp) {
throw new CException('Mailer::compileMultipartTemplate(): '. Yii::t(
'mailer',
'Cannot open file ')
. $path['path']);
}
$image_type = substr(strrchr($path['path'], '.'), 1);
$file = fread($fp, filesize($path['path']));
fclose($fp);
$message_part = '';
switch ($image_type) {
case 'png':
case 'PNG':
$message_part .= "Content-Type: image/png";
break;
case 'jpg':
case 'jpeg':
case 'JPG':
case 'JPEG':
$message_part .= "Content-Type: image/jpeg";
break;
case 'gif':
case 'GIF':
$message_part .= "Content-Type: image/gif";
break;
}
$message_part .= "; file_name = \"" . $path['path'] . "\"\n";
$message_part .= 'Content-ID: <' . $path['cid'] . ">\n";
$message_part .= "Content-Transfer-Encoding: base64\n";
$message_part .= "Content-Disposition: inline; filename = \"" . basename($path['path']) . "\"\n\n";
$message_part .= chunk_split(base64_encode($file)) . "\n";
$multipart .= "--PHP-related-$boundary\n" . $message_part . "\n";
}
$multipart .= "--PHP-related-$boundary\n\n";
}
// Closing compiled email template
$template->body = $multipart. "--PHP-alt-$boundary--\n";
return array($template, $headers);
}
Updated process():
private static function process($to, $template_name, Array $template_vars = array(), Array $template_partials = array(), $action = 'send') {
if (is_string($to)) {
$to = array($to);
}
/** @var $template MailerTemplate */
$template = MailerTemplate::model()->findByAttributes(array(
'name' => $template_name,
));
if (!$template) {
throw new CException(Yii::t('mailer', 'Template does not exists.'));
}
//Template compilation
list($template, $headers) = self::compileMultipartTemplate($template, $template_partials, $template_vars);
$statuses = array();
foreach ($to as $receiver) {
switch ($action) {
case 'send':
$statuses[$receiver] = mail($receiver, $template->subject, $template->body, $headers);
break;
case 'enqueue':
$queue = new MailerQueue;
$queue->to = $receiver;
$queue->subject = $template->subject;
$queue->body = $template->body;
$queue->headers = $headers;
$queue->status = MailerQueue::STATUS_NOT_SENT;
$statuses[$receiver] = $queue->save();
break;
default:
break;
}
}
foreach ($statuses as $email => $status) {
if (!$status) {
Yii::log(Yii::t('app', 'Failed to deliver email: '.$email), 'error');
return false;
}
}
return true;
}
Please let me know if you have any comments on these changes.
Okay, next - are just small comments, maybe you’ll get any useful information from it:
-
I moved Send Preview Email above the iframe, as iframe has large height and it’s not obvious there’s more beneath.
-
I manually updated captions/links everywhere to something more neutral, e.g. ‘Manage templates’
-
I use your module as a nested module, so to get properties values I call
Yii::app()->getModule('admin')->getModule('mailer')->sendEmailLimit
, which is quite long n such case 
-
Also, well, sorry, but I renamed all classes to a shorter variant, e.g. ‘MailerXxxx’, Mailer::process(), hope you won’t mind it 
-
Also, my idea is maybe later (if will be needed) to implement SwiftMailer to be used with your module.
That’s all for now.
Thank you for your module and hope my comments will be helpful somehow.
Back to mailing…