Jump to content

Module: WireMailSwiftMailer


teppo

Recommended Posts

This module integrates Swift Mailer mailing library to ProcessWire, providing support for three different "transports" or methods of sending email: SMTP, Sendmail and Mail (essentially PHP's native mail() function).

WireMail is email-related base class for ProcessWire. See this post by Ryan for the details. Important thing to note here is that a) it's brand new, so as of this writing you'll need a fresh dev version of ProcessWire to use this and b) it makes integrating new ways of handling email-related tasks very easy.

Getting started

You can download or clone the module from GitHub: https://github.com/teppokoivula/WireMailSwiftMailer/.

Using this module is as simple as downloading / cloning it to your modules directory and installing it. If you're going to use SMTP or Sendmail features, insert correct SMTP credentials / Sendmail command to module settings first.

Third transport, Mail, is included simply because it's a native feature of Swift Mailer; if you're going to use it, I would suggest against installing this module. ProcessWire's native WireMail implementation handles this part just fine.

Basic usage

Sending emails should be done using wireMail() function -- if you use mail() directly, you're going to bypass ProcessWire's email handling features entirely. Main difference between mail() and wireMail() is the order and number of arguments:

$number_of_recipients = wireMail($to, $from, $subject, $body);

For more information please take a look at README.

This module is released under GPLv2 (just like ProcessWire itself) with the exception of included Swift Mailer library, which is copyright © Fabien Potencier and released under the MIT license.

  • Like 20
Link to comment
Share on other sites

This is fantastic teppo!

I don't necessarily think you should (as it may make the module unnecessarily complex), but do you have any plans to implement any of the SwiftMailer plugins like Decorator, AntiFlood, and Throttler? I use these for sending out bulk emails to subscribers.

I think perhaps in these cases it is simpler if we just include swiftmailer separately in the appropriate template/module code, but thought I'd raise it in case you have any ideas along these lines.

  • Like 1
Link to comment
Share on other sites

@adrian: I browsed through the plugins just today, wondering if (and how) some of those should be implemented. Not sure yet, but there's undeniably a lot of useful stuff there and it'd be shame not to put any of those in use.

One could always use wireMail() to return instance of WireMailSwiftMailer and it could, in turn, return instance of SwiftMailer.. though admittedly that's not so different from instantiating it yourself :)

  • Like 1
Link to comment
Share on other sites

Thanks Teppo, this is great! I'm looking forward to using this one. I did try to install but ran into some troubles. I was unable to get it to save SMTPServer or SMTPPort. I tracked that down to an issue with field dependencies (in the core, not your module). I've now fixed that in the core, but you may want to remove the requiredIf condition from those two fields if possible, at least temporarily, as I need to do more testing with the core changes I made before committing to GitHub. 

Once I was able to save the SMTPServer and SMTPPort settings, I tried to use the module but keep getting this message:

Fatal error: Class 'Swift_Message' not found in /Volumes/RyMain/Users/ryan/htdocs/cpi/site/modules/WireMailSwiftMailer/WireMailSwiftMailer.module on line 261

It looks like this condition is failing somehow: 

if ($this->transport != "Smtp" || $this->SMTPServer && $this->SMTPPort && $this->senderAddress) 

If I remove that line above (as well as the closing brace further in the function) then I no longer get errors. 

Following that, I configured it for Gmail per the instructions you linked to. The instructions said to choose "SSL/TLS". I chose "TLS" first, but that didn't work. So next I tried "SSL", and that worked. So SMTP setup is now working for me and it seems to work great.

Some other things to mention:

1. The module should not be autoload, if possible. WireMail is intended to be loaded on demand rather than on every request. 

2. The module should not be singular, if possible. When someone makes a call to wireMail(), we want to make sure we're returning them a brand new copy rather than one that might already have some email settings populated in it. That's why singular should be false, so that every time the module is retrieved a new instance is born.

3. The module version number shouldn't have preceding zeros, as this starts PHP thinking it's an octal number or something else (I don't recall). So version number should be 6 rather than 006. 

4. The require() statement in your init() function should likely be a require_once(), and it should ideally include the full path, just in case another copy of of the same directory name exists elsewhere in the PHP path. i.e. 

require_once(dirname(__FILE__) . '/Swift-' . self::SWIFT_VERSION . '/lib/swift_required.php'); 
  • Like 3
Link to comment
Share on other sites

@ryan: thanks, great suggestions!
 
I did notice the issue with saving settings, but had planned to reduce it to a proper test case and submit as an issue. Should've acted faster and disabled it right away, though, to avoid any unnecessary confusion  :)

Autoload and singular state were carried over from the original module, the intention of which was quite different from current implementation. I've taken care of these already, but will have to change the require logic you mentioned, as that admittedly no longer makes sense (and path part would've made sense even in the original form).

Once I was able to save the SMTPServer and SMTPPort settings, I tried to use the module but keep getting this message:

Fatal error: Class 'Swift_Message' not found in /Volumes/RyMain/Users/ryan/htdocs/cpi/site/modules/WireMailSwiftMailer/WireMailSwiftMailer.module on line 261
It looks like this condition is failing somehow: 
if ($this->transport != "Smtp" || $this->SMTPServer && $this->SMTPPort && $this->senderAddress) 
If I remove that line above (as well as the closing brace further in the function) then I no longer get errors.

So far I haven't been able to reproduce this. Will continue testing, though, since if it happened to you it's probably going to happen to others too. Shouldn't be too hard to figure out (*fingers crossed*).

3. The module version number shouldn't have preceding zeros, as this starts PHP thinking it's an octal number or something else (I don't recall). So version number should be 6 rather than 006.

I remember having problems remembering and "getting" the version number logic originally -- 006 was probably carried over from the time I first put this module together. Lately I've used integers, as that seems to be the expected thing there.

Slightly off-topic, perhaps, but integers still seem like a bad fit for version numbers to me. Version numbers should be able to go beyond 0-9 range.. and how do you represent versions such as 1.11.24 as an integer without actually using three sets of numbers?

Of course there's always the option of using strings in those cases, but I find it weird that integers are seen as "the default way" while they are only capable of representing some version numbers properly :)

  • Like 1
Link to comment
Share on other sites

So far I haven't been able to reproduce this. Will continue testing, though, since if it happened to you it's probably going to happen to others too. Shouldn't be too hard to figure out (*fingers crossed*).

Issue was quite obvious, actually: that "if" made sure that Swift Mailer wouldn't get initiated with SMTP transport unless it was properly configured. With the old logic this would've been fine, but not anymore, since local send() method is called regardless of whether Swift Mailer is available. Stupid mistake from me :)

I've changed this so that if you select SMTP transport but don't fill in required params (server and port), the module will fallback to Mail.

This (kind of) brings up another possible issue, though I'm not exactly sure how to deal with that yet: what to do when there's a connection issue in send()? Should that be caught and logged, should I throw an exception.. or is there even better alternative?

So far I'm thinking that an exception would be best way to handle this, so that apps / sites that rely on mail getting won't just continue like nothing happened.. though that might actually cause even more confusion.

Suggestions would be more than welcome :)

Link to comment
Share on other sites

@teppo: I think logging is mandatory, even if you throw an exception.

Also doing an automated fallback from SMTP to php-mail, without the users knowing isn't good in my opinion. Per default I would raise an error (and log it), but would give a configuration setting under advanced (or something) where the user have the possibility to enable that useful behave. If he check that option, _he_ has done so and not _you_, means he has at least be informed about that behave one times. :)

I'm a big fan of (automated) corrections for the user with missconfigurations, but with emails are a million possibilities given. Maybe on a company site they must use SMTP through a special company server to send company-internal messages, it could be that there are very restrictive settings and a mail from php-mail gets blocked and never reaching the recipients. (Sure, a rare case, but may be possible).

  • Like 4
Link to comment
Share on other sites

@horst: thanks for your input.. and your rare case is actually quite familiar for me, so I definitely get that :)

Fallback I mentioned earlier was actually intended as sort of an "installation helper", so that if you install the module on a server where PHP's mail() works (but you just don't want to use it or would prefer to use something else), it won't break anything. Running start, kind of.

Thinking about it now, a better solution for that might be setting the module to use Mail by default.. after that I could safely assume that if SMTP is selected but misconfigured, it's user error and exception should be thrown. This module can, after all, be used for Mail and Sendmail too, even though SMTP seems generally speaking most useful option and Mail definitely the least useful one..

Will have to give this some more thought, though. So far none of the options available seems to be clearly the best way to go. In any case you're definitely right in that it's not always possible (or even sensible for that matter) to attempt correcting mistakes user might've made.

  • Like 2
Link to comment
Share on other sites

I had some problems before that are all fixed now, somehow the following files where not transfered with FTP

/wire/core/WireMail.php

/wire/core/WireMailInterface.php

Installing and sending mail with SMTP worked fine now :)

Thanks for making this module available!

Edited by Raymond Geerts
  • Like 1
Link to comment
Share on other sites

@Raymond: Hi, this sounds that you have get the PW 2.4.0 stable. - If you have the right version, you must see at least a 2.4.1 in the backend

Version 4.3.1 isn't known. Current stable is 2.4.0 and DEV is 2.4.1

Edited by horst
  • Like 2
Link to comment
Share on other sites

Thanks horst, the problem was caused because WireMail and WireMailInterface classes where not uploaded by FTP somehow. Installing and sending works now.

I just only noticed that a form with multiple upload fields only makes the link to the first attachment available. The second attachment is always 0b and has no link. While in the FormBuilder Entries i can see both files uploaded correctly. Is this a bug in WireMail or the SwiftMailer class?

I have a form where the visitor can upload a CV and a Letter (both in doc, docx, pdf, rtf or txt format)

Link to comment
Share on other sites

  • 1 month later...
  • 1 month later...

Hey Teppo, love the module, very smooth.

I did have problems setting the name for the "to" header, ie $mail->toName = $name, or $mail->to( $addr, $name ). Neither seemed to work for me. I've replaced the top of the ___init() function as follows to make this work, may be of some use to you:

public function ___send() {

    foreach( $this->to as $k => $v )
        $toAddr[$k] = $this->toName[$k];

    // create the message
    $message = Swift_Message::newInstance()
        ->setTo($toAddr)
        ->setSubject($this->subject); 

Thanks again for the module  :)

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

When installing module from Module Manager on version 2.4 I get this error on install, and when I try to visit the modules page.

 Error: Class 'WireMail' not found (line 19 of /home/gelburdg/public_html/site/modules/WireMailSwiftMailer/WireMailSwiftMailer.module) 

uploading module manually seeing if it fixes it. 

Link to comment
Share on other sites

@Neeks: which ProcessWire version are you using?

WireMail was introduced in ProcessWire 2.4.1, which means that you still need current dev branch to run it. I'm assuming that this is the problem. Either that, or there's something wrong with your ProcessWire installation (less likely).

Link to comment
Share on other sites

Thanks, sorry about that I just saw "compatability 2.4" under the module page, but after reading this forum post more I realize you do mention I need the latest dev branch. Will make sure to read the whole thread before posting next time. Thanks Teppo. 

Link to comment
Share on other sites

  • 3 weeks later...

@Macrura: sorry for the slow reply, but.. no. This is loosely related to a discussion at GitHub, here: https://github.com/ryancramerdesign/ProcessWire/issues/566. I'd like to hear what Ryan has to say about CC, BCC and attachments before going and putting together custom solution for that.

My opinion is that all of these should be implemented in the base class (WireMail) so that WireMail module authors (at the moment meaning mostly me and Horst) don't have to cook up case-by-case solutions. Even if the modules still required some customisation and couldn't use WireMail features out of the box, at least we'd have compatible implementations.

  • Like 3
Link to comment
Share on other sites

On other news, I've just updated Swift Mailer bundled with the module from 5.0.3 to 5.2.1. This version includes a security fix, so anyone using this module should update. Note that the vulnerability doesn't affect this module; it's related to Reply-To, and this module doesn't, at the moment, use that in any way, but in case that someone is using the Swift Mailer library directly from this module's directory, that could still be a problem.

Other updates added just today include minor fixes and vastly improved tools for testing mailing capabilities (including a tool for sending test messages directly from module's configuration screen).

  • Like 5
Link to comment
Share on other sites

  • 1 month later...

HI Teppo

I am struggling to send mail from my new site - worked fine on my local dev server, but not on the production server.

I have installed your module to try and see if that helps. But when I try and send a test mail (any transport method) it says

 A test message was just sent to 0 recipients

Any thoughts?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...