Commit 5be1bb3d authored by Avris's avatar Avris

Merge branch 'v4.0' into 'master'

v4.0

See merge request !1
parents 08f6302e cff24672
tests/_output/*
vendor/*
.idea/*
/.idea/*
**/.DS_Store
/vendor/*
/tests/_output/*
This diff is collapsed.
......@@ -11,11 +11,11 @@
"homepage": "https://avris.it"
}],
"require": {
"avris/micrus": "^3.0",
"avris/micrus-twig": "^3.0",
"avris/micrus": "^4.0",
"avris/micrus-twig": "^4.0",
"tijsverkoyen/css-to-inline-styles": "^2.2",
"symfony/filesystem": "^3.1",
"phpmailer/phpmailer": "^5.2"
"symfony/lock": "^4.0",
"phpmailer/phpmailer": "^6.0"
},
"autoload": {
"psr-4": { "Avris\\Micrus\\Mailer\\": "src" }
......
mailCss: mail.css
from: ~
fromName: ~
logger:
subject: ~
admins: {}
Avris\Micrus\Mailer\:
dir: '%MODULE_DIR%/src/'
Avris\Micrus\Mailer\Mailer:
public: true
Avris\Micrus\Mailer\MailBuilder:
public: true
Avris\Micrus\Mailer\Sender\SenderInterface: Avris\Micrus\Mailer\Sender\PHPMailerSender
Avris\Micrus\Mailer\Spool\SpoolInterface: Avris\Micrus\Mailer\Spool\MemorySpool
Avris\Micrus\Mailer\Spool\FilesystemSpool:
arguments:
$dir: '%PROJECT_DIR%/var/spool/%APP_ENV%'
<?php
namespace Avris\Micrus\Mailer\Task;
namespace Avris\Micrus\Mailer\Command;
use Avris\Micrus\Console\Task;
use Avris\Micrus\Mailer\Mail\Mail;
use Avris\Micrus\Mailer\Mailer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Filesystem\LockHandler;
use Symfony\Component\Lock\Factory;
use Symfony\Component\Lock\Store\SemaphoreStore;
class SpoolClearTask extends Task
final class SpoolClearCommand extends Command
{
/** @var Mailer */
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
parent::__construct();
}
public function configure()
{
$this
......@@ -24,8 +31,8 @@ class SpoolClearTask extends Task
protected function execute(InputInterface $input, OutputInterface $output)
{
$lock = new LockHandler('mailer:spool');
if (!$lock->lock()) {
$lock = (new Factory(new SemaphoreStore()))->createLock('mailer:spool');
if (!$lock->acquire()) {
$output->writeln('The command is already running in another process.');
return 0;
}
......@@ -39,10 +46,10 @@ class SpoolClearTask extends Task
return 0;
}
/** @var Mailer $mailer */
$mailer = $this->container->get('mailer');
$mailer->clearSpool();
$this->mailer->clearSpool();
$output->writeln('Spool cleared');
$lock->release();
}
}
<?php
namespace Avris\Micrus\Mailer\Task;
namespace Avris\Micrus\Mailer\Command;
use Avris\Micrus\Console\Task;
use Avris\Micrus\Mailer\Mail\Mail;
use Avris\Micrus\Mailer\Mailer;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\LockHandler;
use Symfony\Component\Lock\Factory;
use Symfony\Component\Lock\Store\SemaphoreStore;
class SpoolSendTask extends Task
final class SpoolSendCommand extends Command
{
/** @var Mailer */
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
parent::__construct();
}
public function configure()
{
$this
......@@ -22,25 +32,17 @@ class SpoolSendTask extends Task
protected function execute(InputInterface $input, OutputInterface $output)
{
$lock = new LockHandler('mailer:spool');
if (!$lock->lock()) {
$lock = (new Factory(new SemaphoreStore()))->createLock('mailer:spool');
if (!$lock->acquire()) {
$output->writeln('The command is already running in another process.');
return 0;
}
/** @var Mailer $mailer */
$mailer = $this->container->get('mailer');
$count = 0;
foreach ($mailer->sendOutSpool($input->getOption('limit')) as $i => $status) {
$i = 0;
foreach ($this->mailer->sendOutSpool($input->getOption('limit')) as $status) {
switch ($status->getType()) {
case SpoolStatus::TYPE_COUNT:
$count = $status->getPayload();
$output->writeln(sprintf('%s mails found in the spool', $count));
break;
case SpoolStatus::TYPE_START:
$output->write(sprintf('[%s/%s] ', $i + 1, $count));
$output->write(sprintf('[%s] ', ++$i));
break;
case SpoolStatus::TYPE_SUCCESS:
$output->writeln(sprintf(
......@@ -50,17 +52,24 @@ class SpoolSendTask extends Task
));
break;
case SpoolStatus::TYPE_FAIL:
$output->writeln($status->getPayload() instanceof Mail
? sprintf(
'FAILED sending "%s" to %s',
$payload = $status->getPayload();
if ($payload instanceof Mail) {
$output->writeln(sprintf('FAILED sending "%s" to %s',
$status->getPayload()->getSubject(),
$status->getPayload()->getRecipientsString()
) : sprintf(
'FAILED reading file %s',
$status->getPayload()
));
} elseif ($payload instanceof \Throwable) {
$output->writeln(sprintf('FAILED: %s: %s',
get_class($payload),
$payload->getMessage()
));
} else {
$output->writeln('FAILED');
}
break;
}
}
$lock->release();
}
}
<?php
namespace Avris\Micrus\Mailer\Task;
namespace Avris\Micrus\Mailer\Command;
use Avris\Micrus\Mailer\Mail\Mail;
class SpoolStatus
final class SpoolStatus
{
const TYPE_COUNT = 1;
const TYPE_START = 2;
const TYPE_SUCCESS = 3;
const TYPE_FAIL = 4;
const TYPE_START = 1;
const TYPE_SUCCESS = 2;
const TYPE_FAIL = 3;
/** @var int */
protected $type;
private $type;
/** @var int|Mail */
protected $payload;
private $payload;
public function __construct($type, $payload = null)
public function __construct(int $type, $payload = null)
{
$this->type = $type;
$this->payload = $payload;
}
/**
* @return int
*/
public function getType()
public function getType(): int
{
return $this->type;
}
/**
* @return Mail|int
*/
public function getPayload()
{
return $this->payload;
}
}
\ No newline at end of file
}
<?php
namespace Avris\Micrus\Mailer\Logger;
use Avris\Bag\Bag;
use Avris\Micrus\Mailer\Mail\Address;
use Avris\Micrus\Mailer\Mail\Mail;
use Avris\Micrus\Mailer\Mailer;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\MailHandler;
use Monolog\Logger;
final class MailerHandler extends MailHandler
{
/** @var Mailer */
private $mailer;
/** @var Bag */
private $config;
public function __construct(
Mailer $mailer,
Bag $configMailer_logger,
string $level = Logger::DEBUG,
bool $bubble = true
) {
$this->mailer = $mailer;
$this->config = $configMailer_logger;
parent::__construct($level, $bubble);
}
protected function send($content, array $records)
{
if (empty($this->config->get('admins'))) {
return;
}
$mail = new Mail();
foreach ($this->config->get('admins') as $email => $name) {
$mail->addTo(new Address($email, $name));
}
if ($records) {
$subjectFormatter = new LineFormatter($this->config->get('subject'));
$mail->setSubject($subjectFormatter->format($this->getHighestRecord($records)));
}
$mail->setBody($content);
$this->mailer->send($mail);
}
}
<?php
namespace Avris\Micrus\Mailer\Mail;
class Address implements AddressInterface
final class Address implements AddressInterface
{
/** @var string */
protected $email;
private $email;
/** @var string */
protected $name;
private $name;
/**
* @param string $email
* @param string $name
*/
public function __construct($email, $name = '')
public function __construct(string $email, string $name = '')
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid email', $email));
......@@ -23,23 +19,17 @@ class Address implements AddressInterface
$this->name = $name;
}
/**
* @return string
*/
public function getEmail()
public function getEmail(): string
{
return $this->email;
}
/**
* @return string
*/
public function getName()
public function getName(): string
{
return $this->name;
}
public function __toString()
public function __toString(): string
{
return $this->name ?: $this->email;
}
......
......@@ -3,23 +3,18 @@ namespace Avris\Micrus\Mailer\Mail;
use Avris\Micrus\Exception\InvalidArgumentException;
class Attachment
final class Attachment
{
/** @var string */
protected $path;
private $path;
/** @var string */
protected $filename;
private $filename;
/** @var string */
protected $mimeType;
/**
* @param $path
* @param string $filename
* @throws InvalidArgumentException
*/
public function __construct($path, $filename = '')
private $mimeType;
public function __construct(string $path, string $filename = '')
{
if (!file_exists($path)) {
throw new InvalidArgumentException(sprintf('File "%s" does not exist', $path));
......@@ -34,26 +29,17 @@ class Attachment
finfo_close($finfo);
}
/**
* @return string
*/
public function getPath()
public function getPath(): string
{
return $this->path;
}
/**
* @return string
*/
public function getFilename()
public function getFilename(): string
{
return $this->filename;
}
/**
* @return string
*/
public function getMimeType()
public function getMimeType(): string
{
return $this->mimeType;
}
......
<?php
namespace Avris\Micrus\Mailer\Mail;
class Mail
final class Mail
{
/** @var AddressInterface */
protected $from;
private $from;
/** @var string */
protected $subject;
private $subject;
/** @var string */
protected $body;
private $body;
/** @var string */
protected $altBody;
private $altBody;
/** @var AddressInterface[] */
protected $to = [];
private $to = [];
/** @var AddressInterface[] */
protected $cc = [];
private $cc = [];
/** @var AddressInterface[] */
protected $bcc = [];
private $bcc = [];
/** @var AddressInterface[] */
protected $replyTo = [];
private $replyTo = [];
/** @var Attachment[] */
protected $attachments = [];
private $attachments = [];
/** @var Attachment[] */
protected $embeddedImages = [];
private $embeddedImages = [];
/**
* @return AddressInterface
*/
public function getFrom()
public function getFrom(): ?AddressInterface
{
return $this->from;
}
/**
* @param AddressInterface $from
* @return Mail
*/
public function setFrom($from)
public function setFrom(AddressInterface $from): self
{
$this->from = $from;
return $this;
}
/**
* @return string
*/
public function getSubject()
public function getSubject(): string
{
return $this->subject;
}
/**
* @param string $subject
* @return Mail
*/
public function setSubject($subject)
public function setSubject(string $subject): self
{
$this->subject = $subject;
return $this;
}
/**
* @return string
*/
public function getBody()
public function getBody(): ?string
{
return $this->body;
}
/**
* @param string $body
* @return Mail
*/
public function setBody($body)
public function setBody(string $body): self
{
$this->body = $body;
return $this;
}
/**
* @return string
*/
public function getAltBody()
public function getAltBody(): ?string
{
return $this->altBody;
}
/**
* @param string $altBody
* @return Mail
*/
public function setAltBody($altBody)
public function setAltBody(string $altBody): self
{
$this->altBody = $altBody;
return $this;
}
/**
* @param AddressInterface $address
* @return $this
*/
public function addTo(AddressInterface $address)
public function addTo(AddressInterface $address): self
{
$this->to[] = $address;
return $this;
}
/**
* @return AddressInterface[]
*/
public function getTo()
public function getTo(): array
{
return $this->to;
}
/**
* @param AddressInterface $address
* @return $this
*/
public function addCc(AddressInterface $address)
public function addCc(AddressInterface $address): self
{
$this->cc[] = $address;
return $this;
}
/**
* @return AddressInterface[]
*/
public function getCc()
public function getCc(): array
{
return $this->cc;
}
/**
* @param AddressInterface $address
* @return $this
*/
public function addBcc(AddressInterface $address)
public function addBcc(AddressInterface $address): self
{
$this->bcc[] = $address;
return $this;
}
/**
* @return AddressInterface[]
*/
public function getBcc()
public function getBcc(): array
{
return $this->bcc;
}
public function addReplyTo(AddressInterface $address)
public function addReplyTo(AddressInterface $address): self
{
$this->replyTo[] = $address;
return $this;
}
/**
* @return AddressInterface[]
*/
public function getReplyTo()
public function getReplyTo(): array
{
return $this->replyTo;
}
/**
* @param string $path
* @param string $filename
* @return $this
*/
public function addAttachment($path, $filename = '')
public function addAttachment(string $path, string $filename = ''): self
{
$this->attachments[] = new Attachment($path, $filename);
return $this;
}
/**
* @param string $cid
* @param string $path
* @param string $filename
* @return $this
*/
public function embedImage($cid, $path, $filename = '')
public function embedImage(string $cid, string $path, string $filename = ''): self
{
$this->embeddedImages[$cid] = new Attachment($path, $filename);
return $this;
......@@ -199,7 +156,7 @@ class Mail
/**
* @return Attachment[]
*/
public function getAttachments()
public function getAttachments(): array
{
return $this->attachments;
}
......@@ -207,12 +164,12 @@ class Mail
/**
* @return Attachment[]
*/
public function getEmbeddedImages()
public function getEmbeddedImages(): array
{
return $this->embeddedImages;
}
public function getRecipientsString()
public function getRecipientsString(): string
{
$out = [];
......@@ -231,14 +188,14 @@ class Mail
return join(', ', $out);
}
protected function addressToString(AddressInterface $address)
private function addressToString(AddressInterface $address)
{
return $address->getName()
? sprintf('%s <%s>', $address->getName(), $address->getEmail())
: $address->getEmail();
}
public function __toString()
public function __toString(): string
{
return preg_replace_callback(
'#["\']cid\:([A-Za-z0-9-_]+)["\']#',
......
<?php
namespace Avris\Micrus\Mailer;
use Avris\Micrus\Assetic\AsseticManager;
use Avris\Localisator\LocalisatorInterface;
use Avris\Localisator\Order\LocaleOrderProviderInterface;
use Avris\Micrus\Exception\NotFoundException;
use Avris\Micrus\Mailer\Mail\Address;
use Avris\Micrus\Mailer\Mail\Mail;
use Avris\Bag\Bag;
use Avris\Micrus\Localizator\Localizator;
use Avris\Micrus\View\AssetProvider;
use Avris\Micrus\View\Templater;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;
class MailBuilder
{
/** @var Bag */
protected $config;
private $config;
/** @var Localizator */
protected $localizator;
/** @var LocalisatorInterface */
private $localizator;
/** @var Templater */
protected $templater;
private $templater;
/** @var AsseticManager */
protected $asseticManager;
/** @var string */
private $css;
/** @var CssToInlineStyles */
protected $styleInliner;
private $styleInliner;
/** @var LocaleOrderProviderInterface */
private $orderProvider;
/** @var string */
protected $css;
/**
* @param Bag $config
* @param Localizator $localizator
* @param Templater $templater
* @param AsseticManager $asseticManager
* @param CssToInlineStyles $styleInliner
* @param string $css
*/