<?php
/**
* @copyright Copyright (c) 2016 biceps.ch
*/
namespace Biceps\CoreBundle\Model;
use Biceps\CoreBundle\Entity\ContactPerson;
use Biceps\CoreBundle\Entity\Email;
use Biceps\CoreBundle\Entity\EmailRepository;
use Biceps\CoreBundle\Entity\Invoice;
use Biceps\CoreBundle\Entity\Licence;
use Biceps\CoreBundle\Entity\ProfileRepository;
use Biceps\CoreBundle\Entity\Registration;
use Biceps\CoreBundle\Entity\SpamPrevent;
use Biceps\CoreBundle\Entity\SpamPreventRepository;
use Biceps\CoreBundle\Entity\Voucher;
use Biceps\PaymentBundle\Entity\CamtFile;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use FOS\UserBundle\Util\TokenGenerator;
use Swift_Attachment;
use Swift_Mailer;
use Swift_Message;
use Swift_Mime_Attachment;
use Swift_Transport_SpoolTransport;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Templating\EngineInterface;
use Biceps\CoreBundle\Entity\Profile;
use Biceps\CoreBundle\Entity\User;
use whatwedo\PostFinanceEPayment\Response\Response;
class EmailsManager implements ContainerAwareInterface
{
/** @var \Symfony\Bundle\FrameworkBundle\Templating\EngineInterface */
private $templating;
/** @var Swift_Mailer */
private $mailer;
/** @var ContainerInterface */
protected $container;
/** @var SpamPreventRepository */
protected $spamPreventRepository;
/** @var Router */
protected $router;
/** @var EntityManager */
protected $em;
/**
* @param Swift_Mailer $mailer
* @param EngineInterface $templating
*/
public function __construct(Swift_Mailer $mailer, EngineInterface $templating)
{
$this->templating = $templating;
$this->mailer = $mailer;
}
/**
* Send a message to $email to inform him/her of a $password change.
* @param string $email
* @param string $password
*/
public function sendChangePasswordEmail($email, $password)
{
$this->sendTemplateEmail(
$email,
$this->container->getParameter('biceps_core.emails.password_changed.subject'),
'BicepsCoreBundle:Emails:password_changed',
array('email' => $email, 'password' => $password)
);
}
/**
* Send a message to $email to inform him/her that a new account has been created.
* @param string $email
* @param string $password
*/
public function sendCreateAccountEmail($email, $password)
{
$this->sendTemplateEmail(
$email,
$this->container->getParameter('biceps_core.emails.new_account.subject'),
'BicepsCoreBundle:Emails:email_created',
array('email' => $email, 'password' => $password)
);
}
/**
* Send a message to $email to inform him/her that a new account has been created.
* @param string $email
* @param string $password
*/
public function sendAddAccountEmail($email, $password)
{
$this->sendTemplateEmail(
$email,
$this->container->getParameter('biceps_core.emails.new_account.subject'),
'BicepsCoreBundle:Emails:email_added',
array('email' => $email, 'password' => $password)
);
}
/**
* Send a message to $email to validate his email address using the given $token.
* @param string $email
* @param string $url
* @param string $removeUrl
* @param bool $isGift
*/
public function sendAddressValidationEmail(string $email, string $url, string $removeUrl, $isGift = false)
{
$this->sendTemplateEmail(
$email,
$this->container->getParameter('biceps_core.emails.validate_email.subject'),
'BicepsCoreBundle:Emails:validate_email',
[
'email' => $email,
'url' => $url,
'removeUrl' => $removeUrl,
'isGift' => $isGift,
]
);
}
/**
* Send a message to all the emails of $user to inform that a registration occurred with an email belonging to
* one of his profiles. The emails are not sent if the last notification was sent too recently.
*
* @param User $user
* @param Profile $profile
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendProfileRegistrationAttemptEmail(User $user, Profile $profile)
{
foreach ($user->getEmails() as $email) {
if (!$this->spamPreventRepository->check('registration-attempt', $email->getAddress())) {
// It's not long enough since we sent the same notification email, skip this address
continue;
}
$this->sendTemplateEmail(
$email->getAddress(),
$this->container->getParameter('biceps_core.emails.profile-registration-attempt.subject'),
'BicepsCoreBundle:Emails:profile-registration-attempt',
array('email' => $profile->getEmail())
);
}
}
/**
* @param Email $email
* @param string $password
*
* @return bool
*/
public function sendMigrationAccountInformation(Email $email, string $password)
{
// $spamKey = sprintf('%s:%s', $email->getAddress(), $password);
// if (!$this->spamPreventRepository->check('migration-information', $spamKey)) {
// // It's not long enough since we sent the same notification email, skip this address
// return true;
// }
return $this->sendTemplateEmail(
$email->getAddress(),
$this->container->getParameter('biceps_core.emails.account-migration.subject'),
'BicepsCoreBundle:Emails:account_migration',
[
'user' => $email->getUser(),
'password' => $password,
]
);
}
/**
* Send an email to all the emails of $user to inform that one of his licences has been activated.
*
* @param User $user
* @param Licence $licence
* @param bool $licenceRestarted
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendInformUserOfLicenceActivation(User $user, Licence $licence, $licenceRestarted = false)
{
$email = $user->getMainEmail();
if (!$this->spamPreventRepository->check('licence-activation-' . $licence->getId(), $email->getAddress())) {
// It's not long enough since we sent the same notification email, skip this address
return;
}
if($licenceRestarted){
$this->sendTemplateEmail(
$email->getAddress(),
$this->container->getParameter('biceps_core.emails.licence-reactivation.subject'),
'BicepsCoreBundle:Emails:licence-reactivation',
array('licence' => $licence)
);
}else {
if($licence->isPending()){
$subject = $this->container->getParameter('biceps_core.emails.licence-renewing.subject');
}else{
$subject = $this->container->getParameter('biceps_core.emails.licence-activation.subject');
}
$this->sendTemplateEmail(
$email->getAddress(),
$subject,
'BicepsCoreBundle:Emails:licence-activation',
array('licence' => $licence)
);
}
}
/**
* Send an email to all the emails of $user to inform that one of his licences has been deactivated.
*
* @param User $user
* @param Licence $licence
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendInformUserOfLicenceDeactivation(User $user, Licence $licence)
{
$email = $user->getMainEmail();
if (!$this->spamPreventRepository->check('licence-deactivation-' . $licence->getId(), $email->getAddress())) {
// It's not long enough since we sent the same notification email, skip this address
return;
}
$this->sendTemplateEmail(
$email->getAddress(),
$this->container->getParameter('biceps_core.emails.licence-deactivation.subject'),
'BicepsCoreBundle:Emails:licence-deactivation',
array('licence' => $licence)
);
}
/**
* Send an $invoice PDF as an email attachment.
*
* @param Invoice $invoice
*
* @return bool
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendInvoicePdf(Invoice $invoice)
{
if ($invoice->getOrder()->getUser()) {
$buyer = $invoice->getOrder()->getUser();
$email = $buyer->getMainEmail()->getAddress();
} else {
$buyer = $invoice->getOrder()->getRegistration();
$email = $buyer->getEmail();
}
$filename = sprintf('%s-%s-%s', str_pad($invoice->getId(), 6, '0', STR_PAD_LEFT), date('YmdHis'), 'invoice.pdf');
$path = $this->container->getParameter('kernel.cache_dir') . '/' . $filename;
/** @var InvoiceManager $invoiceManager */
$invoiceManager = $this->container->get('biceps_core.invoice_manager');
$pdf = $invoiceManager->printInvoice($invoice, true);
if (!$pdf->saveAs($path)) {
throw new IOException(sprintf('Unable to save file "%s"', $pdf->getError()), 1545825136);
}
$sent = $this->sendTemplateEmail(
$email,
$this->container->getParameter('biceps_core.emails.invoice-pdf.subject'),
'BicepsCoreBundle:Emails:invoice_pdf',
array('invoice' => $invoice, 'buyer' => $buyer),
Swift_Attachment::fromPath($path, 'application/pdf')
);
// Actually send the email *before* removing the attachment
$this->flushTransport();
if (file_exists($path)) {
unlink($path);
}
return $sent;
}
/**
* Send a voucher PDF as an email attachment.
* @param User|Registration|string $buyer
* @param Voucher $voucher
*/
public function sendVoucherPdf($buyer, Voucher $voucher)
{
$filename = sprintf('%s-%s-%s', str_pad($voucher->getId(), 6, '0', STR_PAD_LEFT), date('YmdHis'), 'bon-biceps.pdf');
$path = $this->container->getParameter('kernel.cache_dir') . '/' . $filename;
$this
->container
->get('biceps_core.pdf_helper')
->generateVoucherPdf($voucher)
->saveAs($path);
$this->sendTemplateEmail(
is_string($buyer) ? $buyer : ($buyer instanceof User ? $buyer->getMainEmail()->getAddress() : $buyer->getEmail()),
$this->container->getParameter('biceps_core.emails.voucher-pdf.subject'),
'BicepsCoreBundle:Emails:voucher_pdf',
['voucher' => $voucher],
Swift_Attachment::fromPath($path, 'application/pdf')
);
// Actually send the email *before* removing the attachment
$this->flushTransport();
if (file_exists($path)) {
unlink($path);
}
}
/**
* Send a list of unsent invoices.
* @param array $invoices
*/
public function sendUnsentInvoicesAlert(array $invoices)
{
if (!$this->spamPreventRepository->check('unsent-invoices-alert', "", 1)) {
// Prevent email to be sent too many time
return false;
}
return $this->sendTemplateEmail(
$this->container->getParameter('notification_admin_email'),
$this->container->getParameter('biceps_core.emails.unsent-invoices.subject'),
'BicepsCoreBundle:Emails:unsent-invoices',
array('invoices' => $invoices)
);
}
/**
* Send a notification to admin
* @param int $reference
* @param string $message
* @param array $data
*
* @return bool
*/
public function sendOrderNotFoundNotification(int $reference, string $message, array $data) : bool
{
if (!$this->spamPreventRepository->check('order-not-found', $reference, 100)) {
// Prevent email to be sent too many time
return false;
}
return $this->sendTemplateEmail(
$this->container->getParameter('notification_admin_email'),
$this->container->getParameter('biceps_core.emails.order-not-found.subject'),
'BicepsCoreBundle:Emails:order-not-found',
[
'message' => $message,
'data' => $data
]
);
}
/**
* Send a registration reminder to the given $registration if none was sent yet.
* Return true if the email was sent and false otherwise.
*
* @param Registration $registration
* @param int $spamDelay
*
* @return bool
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendRegistrationReminder(Registration $registration, $spamDelay = 1)
{
if (!$this->spamPreventRepository->check('registration-reminder', $registration->getId(), $spamDelay)) {
// A reminder has already been sent to this registration
return false;
}
/** @var TokenGenerator $tokenGenerator */
$tokenGenerator = $this->container->get('fos_user.util.token_generator');
$registration->setRemoveToken($registration->getRemoveToken() ?: $tokenGenerator->generateToken());
$registration->setReminderCount($registration->getReminderCount() + 1);
if ($registration->getIsValidated()) {
$url = $this->router->generate(
'biceps_front_register_select_payment_method',
['registration' => $registration->getId()],
UrlGeneratorInterface::ABSOLUTE_URL
);
} else {
$registration->setValidationToken($registration->getValidationToken() ?: $tokenGenerator->generateToken());
$url = $this->router->generate(
'biceps_front_register_validate_email',
[
'token' => $registration->getValidationToken(),
],
UrlGeneratorInterface::ABSOLUTE_URL
);
}
$this->em->persist($registration);
$this->em->flush();
$removeUrl = $this->router->generate(
'biceps_front_register_remove_registration',
[
'token' => $registration->getRemoveToken(),
],
UrlGeneratorInterface::ABSOLUTE_URL
);
$contactUrl = $this->router->generate(
'biceps_front_contact_me',
['reason' => 'account'],
UrlGeneratorInterface::ABSOLUTE_URL
);
$this->sendTemplateEmail(
$registration->getEmail(),
$this->container->getParameter('biceps_core.emails.registration_reminder.subject'),
'BicepsCoreBundle:Emails:registration_reminder',
array(
'registration' => $registration,
'url' => $url,
'removeUrl' => $removeUrl,
'contactUrl' => $contactUrl,
)
);
return true;
}
/**
* Send a renewal reminder to $email for the given $licence.
* Return true if the email was sent and false otherwise.
*
* @param Licence $licence
* @param Email $email
*
* @return bool
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendLicenceRenewalReminder(Licence $licence, Email $email)
{
if (!$this->spamPreventRepository->check('licence-renewal-' . $licence->getId(), $email->getAddress(), 2)) {
// A reminder has already been sent for this licence
return false;
}
$this->sendTemplateEmail(
$email->getAddress(),
$this->container->getParameter('biceps_core.emails.renewal_reminder.subject'),
'BicepsCoreBundle:Emails:renewal_reminder',
array(
'licence' => $licence,
'user' => $email->getUser(),
'renewLink' => $this->router->generate(
'biceps_front_create_order',
array('id' => $licence->getId()),
UrlGeneratorInterface::ABSOLUTE_URL
),
)
);
return true;
}
/**
* Send a expiration reminder to $email for the given $licence.
* Return true if the email was sent and false otherwise.
*
* @param Licence $licence
* @param Email $email
*
* @return bool
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendLicenceExpiredReminder(Licence $licence, Email $email)
{
if (!$this->spamPreventRepository->check('licence-expired-' . $licence->getId(), $email->getAddress(), 100)) {
// A reminder has already been sent for this licence
return false;
}
$this->sendTemplateEmail(
$email->getAddress(),
$this->container->getParameter('biceps_core.emails.expiration_reminder.subject'),
'BicepsCoreBundle:Emails:expiration_reminder',
array(
'licence' => $licence,
'user' => $email->getUser(),
'renewLink' => $this->router->generate(
'biceps_front_create_order',
array('id' => $licence->getId()),
UrlGeneratorInterface::ABSOLUTE_URL
),
)
);
return true;
}
/**
* Send an email to the admin to inform there are errors or warnings in the given CAMT $file.
*
* @param CamtFile $file
* @param array $errors
*/
public function sendCamtErrorsNotifications(CamtFile $file, array $errors = [])
{
$email = $this->container->getParameter('notification_admin_email');
$this->sendTemplateEmail(
$email,
$this->container->getParameter('biceps_core.emails.camt_warnings.subject'),
'BicepsCoreBundle:Emails:camt_warnings',
[
'file' => $file,
'errors' => $errors,
]
);
}
/**
* Send a reminder for the given unpaid $invoice.
* The email template and subject are different depending on whether this is a new customer (registration) or
* an existing customer (user).
*
* @param Invoice $invoice
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendInvoiceFirstReminder(Invoice $invoice)
{
$order = $invoice->getOrder();
$address = null;
$subject = null;
$template = null;
$address = $order->getEmail();
if ($address) {
$user = $order->getUser();
if ($user && !$user->isNew()) {
$subject = $this->container->getParameter('biceps_core.emails.first_reminder_for_users.subject');
$template = 'invoice_first_reminder_for_users';
} else {
$subject = $this->container->getParameter('biceps_core.emails.first_reminder_for_registrations.subject');
$template = 'invoice_first_reminder_for_registrations';
}
$template = 'BicepsCoreBundle:Emails:' . $template;
$filename = sprintf('%s-%s-%s', str_pad($invoice->getId(), 6, '0', STR_PAD_LEFT), date('YmdHis'), 'invoice.pdf');
$path = $this->container->getParameter('kernel.cache_dir') . '/' . $filename;
/** @var InvoiceManager $invoiceManager */
$invoiceManager = $this->container->get('biceps_core.invoice_manager');
$pdf = $invoiceManager->printInvoice($invoice, true);
$pdf->saveAs($path);
$this->sendTemplateEmail(
$address,
$subject,
$template,
array('order' => $order, 'invoice' => $invoice),
Swift_Attachment::fromPath($path, 'application/pdf')
);
// Actually send the email *before* removing the attachment
$this->flushTransport();
if (file_exists($path)) {
unlink($path);
}
}
}
/**
* Send an email to the user/registration with an unpaid invoice to inform the licence has been deactivated or the
* registration has been removed.
*
* @param Invoice $invoice
*
* @throws ORMException
* @throws OptimisticLockException
*/
public function sendInvoiceSecondReminder(Invoice $invoice)
{
$order = $invoice->getOrder();
$address = null;
$subject = null;
$template = null;
$address = $order->getEmail();
if ($address) {
$user = $order->getUser();
if ($user && !$user->isNew()) {
if ($order->getIsGift()) {
$subject = $this->container->getParameter('biceps_core.emails.second_reminder_for_users_for_gift.subject');
} else {
$subject = $this->container->getParameter('biceps_core.emails.second_reminder_for_users.subject');
}
$template = 'invoice_second_reminder_for_users';
} else {
$subject = $this->container->getParameter('biceps_core.emails.second_reminder_for_registrations.subject');
$template = 'invoice_second_reminder_for_registrations';
}
$template = 'BicepsCoreBundle:Emails:' . $template;
$filename = sprintf('%s-%s-%s', str_pad($invoice->getId(), 6, '0', STR_PAD_LEFT), date('YmdHis'), 'invoice.pdf');
$path = $this->container->getParameter('kernel.cache_dir') . '/' . $filename;
/** @var InvoiceManager $invoiceManager */
$invoiceManager = $this->container->get('biceps_core.invoice_manager');
$pdf = $invoiceManager->printInvoice($invoice, true);
$pdf->saveAs($path);
$this->sendTemplateEmail(
$address,
$subject,
$template,
array('order' => $order, 'invoice' => $invoice),
Swift_Attachment::fromPath($path, 'application/pdf')
);
// Actually send the email *before* removing the attachment
$this->flushTransport();
if (file_exists($path)) {
unlink($path);
}
}
}
/** {@inheritdoc} */
public function setContainer(ContainerInterface $container = null)
{
/** @var EntityManager $entityManager */
$this->em = $container->get('doctrine.orm.entity_manager');
$this->container = $container;
$this->spamPreventRepository = $this->em->getRepository(SpamPrevent::class);
$this->router = $container->get('router');
}
/**
* Send an email based on a twig template.
* Will use $template.html.twig for the HTML version and $template.txt.twig for the text version.
* @param string $recipient
* @param string $subject
* @param string $template
* @param array $params
* @param Swift_Mime_Attachment $attachment
* @param callable $beforeSend
*
* @return bool
*/
protected function sendTemplateEmail(
$recipient,
$subject,
$template,
array $params = array(),
Swift_Mime_Attachment $attachment = null,
$beforeSend = null
)
{
$bodyHtml = $this->templating->render(sprintf('%s.html.twig', $template), $params);
$bodyTxt = $this->templating->render(sprintf('%s.txt.twig', $template), $params);
return $this->sendEmail($recipient, $subject, $bodyHtml, $bodyTxt, $attachment, $beforeSend);
}
/**
* Send an email.
* @param string $recipient
* @param string $subject
* @param string $bodyHtml
* @param string $bodyTxt
* @param Swift_Mime_Attachment $attachment
* @param callable $beforeSend
*
* @return bool
*/
protected function sendEmail(
$recipient,
$subject,
$bodyHtml,
$bodyTxt,
Swift_Mime_Attachment $attachment = null,
$beforeSend = null
)
{
/** @var Swift_Message $message */
$message = (new Swift_Message())
->setSubject($subject)
->setFrom($this->container->getParameter('mails_sender'))
->setTo($recipient)
->setBody($bodyHtml, 'text/html')
->addPart($bodyTxt, 'text/plain');
$replyTo = $this->container->getParameter('mails_reply_to');
if ($replyTo){
$message->setReplyTo($replyTo);
}
if ($attachment) {
$message->attach($attachment);
}
if ($beforeSend && is_callable($beforeSend)) {
call_user_func($beforeSend, $message);
}
return $this->mailer->send($message) === 1;
}
/**
* Flush the transport so the messages are sent immediately.
* See http://stackoverflow.com/a/15643422
*/
protected function flushTransport()
{
/** @var Swift_Transport_SpoolTransport $transport */
$transport = $this->container->get('mailer')->getTransport();
/** @var Swift_Transport_SpoolTransport $transportReal */
$transportReal = $this->container->get('swiftmailer.transport.real');
$spool = $transport->getSpool();
$spool->flushQueue($transportReal);
}
/**
* @param ContactPerson $person
*
* @return bool
*/
public function sendContactAdministratorEmail(ContactPerson $person)
{
/** @var EmailRepository $emailRepository */
$emailRepository = $this->em->getRepository(Email::class);
$emailOrProfile = $emailRepository->findOneByAddress($person->getEmail());
if (!$emailOrProfile) {
/** @var ProfileRepository $profileRepository */
$profileRepository = $this->em->getRepository(Profile::class);
$emailOrProfile = $profileRepository->findOneByEmail($person->getEmail());
}
return $this->sendTemplateEmail(
$this->container->getParameter('contact_admin_email'),
$this->container->getParameter('biceps_core.emails.contact-me.subject'),
'BicepsCoreBundle:Emails:contact_me',
[
'data' => $person,
'user' => $emailOrProfile ? $emailOrProfile->getUser() : null,
],
null,
function (Swift_Message $message) use ($person) {
$message->addReplyTo($person->getEmail(), $person->getFullName());
}
);
}
}