ryan Posted March 1, 2014 Share Posted March 1, 2014 Lots of people have been asking for a way for ProcessWire to support sending of email, outside of just using PHP's mail() function. I haven't really wanted to expand the scope of ProcessWire that deep into email sending, but I have been wanting to setup a way so that people could override how emails are sent, with modules. For people that are interested in making other ways of sending email in ProcessWire, I've setup a new module base class called WireMail, and a new function called wireMail(). The wireMail() function will use whatever WireMail module is installed. If none is installed, then it will use PW's default WireMail implementation (based on PHP's default mail function). The wireMail() function replaces all instances of PHP's mail() function in ProcessWire's source. It works in a similar way as PHP's mail() except that supports a few different usages. Standard usage would be this: // to, from, subject, body wireMail('user@domain.com', 'ryan@runs.pw', 'Mail Subject', 'Mail Body'); Another usage would be to give it no arguments, and it'll return whatever WireMail module is installed for you to use yourself. If no WireMail module is installed, then it returns ProcessWire's WireMail. $mail = wireMail(); $mail->to('user@hi.com')->from('ryan@runs.pw'); // all calls can be chained $mail->subject('Mail Subject'); $mail->body('Mail Body'); $mail->bodyHTML('<html><body><h1>Mail Body</h1></body></html>'); $mail->send(); Since all of this stuff is only on the PW 2.4 dev branch at present, I'm posting this primarily for people that are interested in creating WireMail modules. For instance, I know that both Teppo and Horst (and perhaps others?) have put together such modules and ideas, so this is all aimed at coming up with a way for those ideas to be easily integrated into PW by way of modules. To make your own WireMail module, you simply create a module that extends WireMail and provide your own implementation for the send() method. Of course, you can go further than that, but that's all that is technically necessary. I've attached an example module called WireMailTest that demonstrates a WireMail module. When installed, it is used rather than PW's WireMail. This WireMailTest module includes lots of comments for you in the code of it, and you may find it helpful to use it as your starting point. WireMailTest.module For you guys developing modules, please throw any questions my way and I'm happy to help. Likewise, let me know if you think I'm missing anything important in the base interface that the modules are based upon and we can update it. 29 Link to comment Share on other sites More sharing options...
Soma Posted March 1, 2014 Share Posted March 1, 2014 Great thanks Ryan. This should have been in 2.4 stable Link to comment Share on other sites More sharing options...
cstevensjr Posted March 1, 2014 Share Posted March 1, 2014 Wow!! Link to comment Share on other sites More sharing options...
teppo Posted March 1, 2014 Share Posted March 1, 2014 Got first version of SwiftMailer implementation working locally, should be able to push to GitHub after some testing. Ryan has, once again, made things too easy for us.. 9 Link to comment Share on other sites More sharing options...
teppo Posted March 1, 2014 Share Posted March 1, 2014 @ryan: just noticed that use case 3, "specify both $body and $bodyHTML as arguments, but no $options", doesn't seem to work as described. Later $options is used as an argument of array_merge(), which means that it has to be an array itself. Link to comment Share on other sites More sharing options...
teppo Posted March 1, 2014 Share Posted March 1, 2014 I can't seem to break it anymore myself, so I've just pushed Swift Mailer module to GitHub. It's still far from complete and probably lacking lots of important stuff, but if anyone wants to give it a try already please do so.. and let me know how it went. I'll create an "official" thread when it feels slightly more polished 5 Link to comment Share on other sites More sharing options...
horst Posted March 1, 2014 Share Posted March 1, 2014 (edited) Ryan, I would like to have the possibility not to use only the Emailaddress but also the Recipients Names with the TO-array. Actually it accepts only a single emailaddress or an array with emailaddresses, but no names. When building that on my own I would have to break compatibility. Don't like that. Could we not use something like: public function to($email) { if(!is_array($email)) $email = explode(',', $email); $this->mail['to'] = array(); // clear // check for key-value pairs containing name=>email if(array_keys($email) !== range(0, count($email) - 1)) { foreach($email as $n=>$e) { $this->mail['to'][$n] = $this->sanitizeEmail($e); } } else { foreach($email as $e) { $this->mail['to'][] = $this->sanitizeEmail($e); } } return $this; } // also within the send() function we have to check if we have array with name=>email or string only with email I'm not happy with my code example. A better way is much appreciated, but I really would like to have that possibility. ---------------------------------------------------------- - EDIT ---------------------------------------------------------- Now after some testing I came up with this code what seems to be better: public function to($email) { if(!is_array($email)) $email = explode(',', $email); $this->mail['to'] = array(); // clear foreach($email as $n=>$e) { // check for associative key-value pairs containing name=>email if(is_string($n)) { $this->mail[$type][$this->sanitizeHeader($n)] = $this->sanitizeEmail($e); } else { $this->mail['to'][] = $this->sanitizeEmail($e); } } return $this; } And in the send() function we need to loop like this: $numSent = 0; foreach($this->to as $n=>$to) { $to = !is_string($n) ? $to : ( $n . ' <' . $to . '>' ); if(mail($to, $this->subject, $body, $header, $param)) $numSent++; } Edited March 3, 2014 by horst 4 Link to comment Share on other sites More sharing options...
Raymond Geerts Posted March 3, 2014 Share Posted March 3, 2014 This is realy great, thanks for implementing this feature. And ofcourse to all who make modules upon it. Does it also mean that when this module is installed, the FormBuilder will be able to send mail through this module 'out of the box'? Link to comment Share on other sites More sharing options...
teppo Posted March 3, 2014 Share Posted March 3, 2014 Does it also mean that when this module is installed, the FormBuilder will be able to send mail through this module 'out of the box'? FormBuilder needs to be updated to use wireMail() instead of mail() first, so not exactly out of the box, but I'm sure Ryan will take care of this 1 Link to comment Share on other sites More sharing options...
sozoclive Posted March 3, 2014 Share Posted March 3, 2014 Thank you for implementing this. It means a lot. Link to comment Share on other sites More sharing options...
renobird Posted March 3, 2014 Share Posted March 3, 2014 Ryan, you are on fire! Link to comment Share on other sites More sharing options...
ryan Posted March 3, 2014 Author Share Posted March 3, 2014 @ryan: just noticed that use case 3, "specify both $body and $bodyHTML as arguments, but no $options", doesn't seem to work as described. Later $options is used as an argument of array_merge(), which means that it has to be an array itself. Thanks, just pushed a fix for this. Ryan, I would like to have the possibility not to use only the Emailaddress but also the Recipients Names with the TO-array. Actually it accepts only a single emailaddress or an array with emailaddresses, but no names. Good point! While I almost never use a "TO: name" (and many email clients, including gmail, don't even display it), it does make sense to support it. I've just updated it to support this. I went a little bit different route than you mentioned though, because there's always a chance two recipients might have the same name, but not likely they would have the same email. So I didn't think name would be workable as an array index. I also wanted to maintain consistency with how it's storing 'from' and 'fromName' separately, so the 'to' and 'toName' are stored in separate arrays. The 'toName' array is indexed by the email address. So now if you want to support use of 'toName' in your send() method, you'd do this: foreach($this->to as $email) { $name = $this->toName[$email]; if($name) { // send to: Name <$email> } else { // send to: $email } } If you don't need/want to support the "TO: name" (as in WireMailTest) then you don't have to do anything, as the means by which you access and iterate $this->to has not changed. As for how to supply to "TO: name" from the API side, you can do it any of these ways: // you can also use a string (or CSV string for multiple), like you would with PHP mail wireMail('User Name <user@example.com>', $from, $subject, $body); // array may be simplest if sending to multiple email addresses wireMail(array('user@example.com' => 'User Name'), $from, $subject, $body); // from the object side you can supply it as an optional 2nd argument to the to() method $mail = wireMail(); $mail->to('user@example.com', 'User Name'); // or in an array $mail->to(array('user@example.com' => 'User Name')); // or as a string (or CSV string for multiple) $mail->to('User Name <user@example.com>'); // the WireMail::from has also been updated to support a second argument or string $mail->from('sender@example.com', 'Sender Name'); $mail->from('Sender Name <sender@example.com>'); One other change is that the to() method no longer clears out any existing to() addresses on every call. So you don't have to supply all of your to: addresses in the single function call, you can call it multiple times, each with a separate email address if preferred. But if you do want to clear out the list, then just call the to() method with no arguments and it'll clear them. Does it also mean that when this module is installed, the FormBuilder will be able to send mail through this module 'out of the box'? Yes, once we've merged the WireMail class into the master branch (stable), then I'll put out an updated FormBuilder that uses wireMail() rather than mail(). Though if anyone needs it sooner, I'll be happy to post an updated FormBuilderEmail class that uses wireMail(). 4 Link to comment Share on other sites More sharing options...
kongondo Posted March 3, 2014 Share Posted March 3, 2014 Thanks for the update Ryan! Great job! Link to comment Share on other sites More sharing options...
horst Posted March 3, 2014 Share Posted March 3, 2014 Ryan, this is awesome! Many thanks for this magical email handling Link to comment Share on other sites More sharing options...
cstevensjr Posted March 3, 2014 Share Posted March 3, 2014 Yes, once we've merged the WireMail class into the master branch (stable), then I'll put out an updated FormBuilder that uses wireMail() rather than mail(). Though if anyone needs it sooner, I'll be happy to post an updated FormBuilderEmail class that uses wireMail(). Can you please post an updated FormBuilderEmail class that uses wireMail() sooner rather than later. Thanks. 1 Link to comment Share on other sites More sharing options...
Pete Posted March 18, 2014 Share Posted March 18, 2014 Teppo - SwiftMailer works well with Mandrill - using this now, thanks 2 Link to comment Share on other sites More sharing options...
horst Posted March 19, 2014 Share Posted March 19, 2014 @Ryan: what do you think of adding a method to the WireMail class that tells the user if the currently active Module can send attachments? if(wireMail()->canSendAttachments()) { wireMail()->attachment($file)->to($to)->send(); } Link to comment Share on other sites More sharing options...
Pete Posted April 15, 2014 Share Posted April 15, 2014 Can we have options for CC and BCC please ryan (and then Teppo in SwiftMailer )? It's not mega urgent, but these do come in handy in a few situations. 2 Link to comment Share on other sites More sharing options...
horst Posted April 15, 2014 Share Posted April 15, 2014 (edited) Can we have options for CC and BCC please ryan (and then Teppo in SwiftMailer )? It's not mega urgent, but these do come in handy in a few situations. @Pete: you can ask Teppo directly. He (technically *) can implement it in SwiftMailer without any change in WIreMail base class. I have done it for bcc and cc. It is not more than to add these two as public functions to the SwiftMailer module and make them handle the according SwiftMailer calls for that headers. Simple! * don't know about his time budget nor if he ever want it Edited April 15, 2014 by horst 1 Link to comment Share on other sites More sharing options...
Pete Posted April 15, 2014 Share Posted April 15, 2014 The problem with just implementing it in Swiftmailer (or in your class) is this from ryan's first post: If none is installed, then it will use PW's default WireMail implementation (based on PHP's default mail function). So I can't count on the user not just using the default WireMail class and certain functionality not being available. I think that the WireMail base class needs to have these functions set up before subclasses (is that the correct term?) implement features or we'll have issues with things working consistently or we'll have to do some checks to see which Mail module is installed. My usage in this case is writing modules that use the WireMail class, so I need the functionality in the base class to be sure that the emails will definitely send 1 Link to comment Share on other sites More sharing options...
teppo Posted April 15, 2014 Share Posted April 15, 2014 I haven't had much (enough) time to work on my mailer module and haven't looked at how WireMailSMTP handles these particular things, but in general I'd have to agree with Pete. For things that are commonly used, it'd be best if there was a "standard" way to do that. One that doesn't depend on which module extending WireMail is installed at the time. I believe that we're talking about interfaces here, though that's not technically what it's going to be.. or what it is at the moment, at least. Then again, if @horst has implemented this feature already, I'll probably take a look at his implementation anyway and use similar public methods if possible to provide some consistency 2 Link to comment Share on other sites More sharing options...
horst Posted April 15, 2014 Share Posted April 15, 2014 I think Ryans initial intention was to support the common basic usage and give us an easy to use possibilty to cretae submodules that can _extend_ the base class. But I am definitly not against extending the base class with cc, bcc and attachment 1 Link to comment Share on other sites More sharing options...
Pete Posted April 15, 2014 Share Posted April 15, 2014 I'm going to highlight this again horst as it's the important bit If none is installed, then it will use PW's default WireMail implementation (based on PHP's default mail function). So whether yours or Teppo's module is installed or neither, emails can still be sent using the default Wiremail class. Therefore if I'm writing a module that requires email sending functionality, it needs to be able to use the same functions whether it's ryan's base module, the SwiftMailer class or your own module or module authors won't know what to expect. I think these WireMail modules need to be coordinated somewhat so that whatever the base class supports, the other classes also support so there are no surprises. 2 Link to comment Share on other sites More sharing options...
horst Posted April 15, 2014 Share Posted April 15, 2014 @Pete: we are on the same side (in german: "Du rennst bei mir offene Türen ein") But I am definitly not against extending the base class with cc, bcc and attachment +1 for implementing cc, bcc and attachment into the WireMail base class! Regarding attachments: https://processwire.com/talk/topic/5704-module-wiremailsmtp/#entry56631 2 Link to comment Share on other sites More sharing options...
Nico Knoll Posted July 29, 2014 Share Posted July 29, 2014 Is bcc already implemented? Going to recode my Newsletter module and would need this functionality. Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now