CakePHP 3.x - Email to log instead of sending during debug - email

I would like to switch my application to a configuration where email isn't actually send, but instead saved to a log file.
This way I can test my application normally without being afraid of accidentally emailing to hundreds of users and without spamming myself.
I figured something with EmailTransports could be a solution. For instance, when using the DebugTransport the emails aren't send at all, the mail content is instead only returned by the ->send() function.
The downside of this transport is than I have to modify controller code in order to display the content, which I would like to avoid.
So is there a configuration such that email is stored to files instead of being sent, e.g.:
[root]
logs/
emails/
2019-10-01_15:32_email#example.com.txt
2019-10-01_16:54_another_recipient#example.com.txt
...

There is no such built-in configuration, no, but you can easily create your own custom transport that logs emails to files instead of sending them.
Here's a very basic example transport that extends the debug transport, and writes the data to a custom logging scope:
namespace App\Mailer\Transport;
use Cake\Log\LogTrait;
use Cake\Mailer\Email;
use Cake\Mailer\Transport\DebugTransport;
use Psr\Log\LogLevel;
class TestTransport extends DebugTransport
{
use LogTrait;
public function send(Email $email)
{
$data = parent::send($email);
$this->log(json_encode($data), LogLevel::DEBUG, ['scope' => ['emails']]);
return $data;
}
}
See also
Cookbook > Email > Using Transports > Creating Custom Transports

Related

Symfony Messenger: Send logged messenger errors per email (via swift_mailer)?

I've configured monolog to send errors via email as described in the symfony docs here: https://symfony.com/doc/4.3/logging/monolog_email.html
Works well with all errors happing during a request, as well as console command errors.
But it does not send emails for errors which occurred during the handling of a messenger message.
Errors are shown when running the consumer bin/console messenger:consume async -vv and they also show up in prod.log like this:
[2020-01-10 12:52:38] messenger.CRITICAL: Error thrown while handling message...
Thanks for any hints on how to set up monolog to get messenger errors emailed too.
In fact monolog swift_mailer type use SwiftMailerHandler
wish also implements reset interface and use memory spool by default wish keep all emails in buffer until it is destructed, so till the end of request :
onKernelTerminate
onCliTerminate
OR till reset method is called, which means that for messenger worker no emails will be send ever because ther's no instant flush - all of them will be kept in in-memory buffer, and probably lost if the process will be killed.
To solve this, you can just disable the default spool memory setting for swiftmailer.
Another solution is to flush your emails after WorkerMessageFailedEvent event gets fired, you can implement an event subscriber to do it for this.
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
use Symfony\Contracts\Service\ResetInterface;
/**
* Class ServiceResetterSubscriber.
*/
class ServiceResetterSubscriber implements EventSubscriberInterface
{
protected ResetInterface $servicesResetter;
public function __construct(ResetInterface $servicesResetter)
{
$this->servicesResetter = $servicesResetter;
}
public function resetServices(): void
{
$this->servicesResetter->reset();
}
public static function getSubscribedEvents(): array
{
return [
WorkerMessageFailedEvent::class => ['resetServices', 10],
];
}
}
Register your service with the right argument:
App\EventSubscriber\ServiceResetterSubscriber:
arguments: ['#services_resetter']
By the way without this (and without buffer limit) your app will leak and no emails will be sent ever.
Another trick:
Make sure that your message implements \JsonSerializable to get the message content in your logs, because messenger uses his monolog directly and its context serializer wish use json_encode for seriliazation.
That's why we need to customize their JSON representation when encoded is done with json_encode.

Jenkins email-ext plugin thinks I have no recipients configured

I'm using Jenkins 2.2 and email-ext plugin 2.42 (both current, as are all of the rest of my plugins). I have my global configuration set to have a single, explicit recipient and my project is using default email notification configuration (that is, send to $DEFAULT_RECIPIENTS). I have also set an explicit recipient in the project. In both configurations, the console output for the job says:
An attempt to send an e-mail to empty list of recipients, ignored.
This would seem to be https://issues.jenkins-ci.org/browse/JENKINS-13583 except
1. that was marked as resolved four years ago, and 2. I get e-mail when I use basic, built-in notifications. Does anyone else see this problem with email-ext?
Turns out plugin configuration is somewhat non-intuitive; a necessary setting is buried behind an Advanced button. I got answers in https://issues.jenkins-ci.org/browse/JENKINS-34731 and it is working now as follows:
In the Advanced settings, Triggers -> Failure - Any lists "Developers" by default, but not "Recipient List."
For those using this plugin in combination with Job DSL. I have do add the sendTo { recipientList() } block explicitly to the different triggers.
So my DSL looked like this:
extendedEmail {
recipientList('${EMAIL_RECIPIENTS}')
triggers {
failure {
subject('The subject')
content("The content")
sendTo {
recipientList()
}
}
}
}
Instead of using $DEFAULT_RECIPIENTS use to:
emailext(
to: 'somename#emailprovider.com',
body: '${DEFAULT_CONTENT}',
mimeType: 'text/html',
subject: '${DEFAULT_SUBJECT}',
replyTo: '$DEFAULT_REPLYTO'
)
}
Ref: https://stackoverflow.com/a/39499554/1134084
I finally found the problem through repeated attempts. There is no need for such trouble at all. The reason is that in the advanced Settings of Editable Email Notification trigger condition, the Recipient List is empty by default, and all your Settings outside will be overridden. An attempt to send an e-mail to empty list of recipients was ignored. An attempt to send an E-mail to empty list of recipients ignored.

Zend Mail - Where to instantiate it?

I'm using a Data Mapper / Gateway design pattern.
So I have a:
Mapper;
Gateway;
Domain Object (mainly with getters and setters);
A controller;
A view.
My question is: where should I instantiate the Zend Mail ?
I believe the view is obviously out of question, and the gateway is, as well, not to be considered.
The controller should be kept clean, so:
Mapper our Domain Object ?
If our form will have some select box that will retrieve data from the database, then, perhaps the Mapper will be the most appropriate place to instantiate Zend Mail ?
Thanks
Hmmmm? Well with Zend you can configure your Zend_Mail in your bootstrap or by using the application.ini file or some other config file. That's how I configure mine right now. For dev, I'll write the mails to a file and for testing I'll do mail over an actual mail server.
I instantiate my Zend_Mail instance in a class that I call Mail_Service. This mail service class will create a Zend_Mail instance internally when it needs to send a mail and will use an existing Zend_Mail instance if one has been created and more mails need to be sent.
It has methods that will send predefined mails for me. For example,
Mail_Service->sendWelcomeEmail( $userInfo )
OR
Mail_Service->sendActivationEmail( $userInfo )
Say for example my controller gets a request to create a new user, then the over all flow of my code will be like this
//in the controller
//process form from browser somehow
UserAccountService->createNewUser( $userInfo );
/////////////////
/// Within the user account service
public function createNewUser( $userInfo )
{
$userMapper->createNewUser( $userInfo );
$preferencesMapper->createDefaultPreferencesForUser( $userInfo );
MailService->sendWelcomeEmail( $userInfo );
}
I don't know if this is the best way to do it, but this way my service have function
names that are relevant to the service and capture a whole work flow instead of being atomic operations that just forward calls to other objects.
Hope this helps.
I always keep code that sends mail in my controllers.
Model - database/business logic
View - html / presentation layer
Controller - The code that does stuff.

How to implement a script that listens for new mail and then perform an action

I have an apache James mail server setup and I would like to create a script that will listen for new mail and then communicate with an Asterisk server to call a user phone number and read the message. I assume this is possible but cannot find any information. I have been using perl scripting for other tasks so if I could do this with perl that would be great.
Thanks in advance
A solution would be to set up a cron job to run a perl script that uses something like Mail::POP3Client to check for new messages and sends something to Asterisk using (maybe) Asterisk::AGI. If the CPAN modules don't do what you need, you can always have the perl script execute system calls to interface with command-line tools that are capable of checking POP or interfacing with Asterisk.
I guess the best way to do that is to write a Mailet in java and put it in configuration to make it listen for the all mails in the root processor.
For Apache James 3.0-beta5 steps would be something like this:
Write the Mailet i.e:
import org.apache.mailet.*;
public class myMailet extends GenericMailet {
private String aParameter;
#Override
public void init(MailetConfig cfg) throws MessagingException {
super.init(cfg);
aParameter = getInitParameter("myNeatParameter"); // use this if you need to use some parameters specified inside the mailetcontainer.xml
}
#Override
public void service(Mail email) throws MessagingException {
doYourThingWith(email);
}
private void doYourThingWith(Mail email){
// TODO something with the email
}
}
Build the Mailet into a jar file
Add the resulting jar file to /conf/lib folder
Edit /conf/mailetcontainer.xml file where you would add this:
<mailet match="All" class="myMailet">
<myNeatParameter>some value</myNeatParameter>
</mailet>
as a child element to the root processor (or whatever you think is proper).
Restart James
Well it's not a script, but it is very good (if not even the best) solution.
You could use Mail::POP3Client to poll the mailbox for messages. Or if the mailbox supports IMAP and you want to continuously monitor it, use an IMAP module, perhaps Net::IMAP::Simple ?
I just googled for [Asterisk Perl] and a guide called "How to Write a Perl AGI Application" appeared on the first page. It describes how to use the Asterisk::AGI module to connect to an Asterisk server using Perl.
Finally, you'll need a text-to-speech application. I'm sure there are many of these available. They probably won't be written in Perl, but there may be existing Perl interfaces for them.

What is the best way to log errors in Zend Framework 1?

We built an app in Zend Framework (v1) and have not worked a lot in setting up error reporting and logging. Is there any way we could get some level or error reporting without too much change in the code? Is there a ErrorHandler plugin available?
The basic requirement is to log errors that happens within the controller, missing controllers, malformed URLs, etc.
I also want to be able to log errors within my controllers. Will using error controller here, help me identify and log errors within my controllers? How best to do this with minimal changes?
I would use Zend_Log and use the following strategy.
If you are using Zend_Application in your app, there is a resource for logging. You can read more about the resource here
My advice would be to choose between writing to a db or log file stream. Write your log to a db if you plan on having some sort of web interface to it, if not a flat file will do just fine.
You can setup the logging to a file with this simple example
resources.log.stream.writerName = "Stream"
resources.log.stream.writerParams.stream = APPLICATION_PATH "/../data/logs/application.log"
resources.log.stream.writerParams.mode = "a"
resources.log.stream.filterName = "Priority"
resources.log.stream.filterParams.priority = 4
Also, I would suggest sending Critical errors to an email account that is checked regularly by your development team. The company I work for sends them to errors#companyname.com and that forwards to all of the developers from production sites.
From what I understand, you can't setup a Mail writer via a factory, so the resource won't do you any good, but you can probably set it up in your ErrorController or Bootstrap.
$mail = new Zend_Mail();
$mail->setFrom('errors#example.org')
->addTo('project_developers#example.org');
$writer = new Zend_Log_Writer_Mail($mail);
// Set subject text for use; summary of number of errors is appended to the
// subject line before sending the message.
$writer->setSubjectPrependText('Errors with script foo.php');
// Only email warning level entries and higher.
$writer->addFilter(Zend_Log::WARN);
$log = new Zend_Log();
$log->addWriter($writer);
// Something bad happened!
$log->error('unable to connect to database');
// On writer shutdown, Zend_Mail::send() is triggered to send an email with
// all log entries at or above the Zend_Log filter level.
You will need to do a little work to the above example but the optimal solution would be to grab the log resource in your bootstrap file, and add the email writer to it, instead of creating a second log instance.
You can use Zend_Controller_Plugin_ErrorHandler . As you can see on the documentation page there is an example that checks for missing controller/action and shows you how to set the appropriate headers.
You can then use Zend_Log to log your error messages to disk/db/mail.