Add e-mail webhook.
This commit is contained in:
parent
92fbac4006
commit
ace826dae5
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -5,10 +5,13 @@ release channel, you can take advantage of these new features and fixes.
|
||||||
|
|
||||||
## New Features/Changes
|
## New Features/Changes
|
||||||
|
|
||||||
- **Self-Service Password Reset**: If SMTP is configured properly in the system settings page, users can now request a
|
- **E-mail Delivery**: System administrators can now configure SMTP for e-mail delivery via the system settings page. If
|
||||||
password reset directly from the login page instead of requiring administrator intervention. The password reset
|
SMTP is enabled for your installation, the following functionality is added:
|
||||||
functionality creates a single-time-use login token that can be used to reset the user's password and log them in
|
|
||||||
once.
|
- **Self-Service Password Reset**: Users can request a password recovery token to reset their own passwords.
|
||||||
|
|
||||||
|
- **E-mail Web Hook**: You can dispatch an e-mail to specified recipients as a web hook when specific triggers
|
||||||
|
occur.
|
||||||
|
|
||||||
- Web Hooks can now be triggered to dispatch when a station goes offline or comes online.
|
- Web Hooks can now be triggered to dispatch when a station goes offline or comes online.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @var array $triggers
|
||||||
|
* @var App\Environment $environment
|
||||||
|
* @var App\Http\Router $router
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
'method' => 'post',
|
||||||
|
|
||||||
|
'groups' => [
|
||||||
|
|
||||||
|
'message_grp' => [
|
||||||
|
'use_grid' => true,
|
||||||
|
'elements' => [
|
||||||
|
|
||||||
|
'name' => [
|
||||||
|
'text',
|
||||||
|
[
|
||||||
|
'label' => __('Web Hook Name'),
|
||||||
|
'description' => __(
|
||||||
|
'Choose a name for this webhook that will help you distinguish it from others. This will only be shown on the administration page.'
|
||||||
|
),
|
||||||
|
'required' => true,
|
||||||
|
'form_group_class' => 'col-md-6',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'triggers' => [
|
||||||
|
'multiCheckbox',
|
||||||
|
[
|
||||||
|
'label' => __('Web Hook Triggers'),
|
||||||
|
'options' => $triggers,
|
||||||
|
'required' => true,
|
||||||
|
'form_group_class' => 'col-sm-12',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'to' => [
|
||||||
|
'text',
|
||||||
|
[
|
||||||
|
'label' => __('Message Recipient(s)'),
|
||||||
|
'belongsTo' => 'config',
|
||||||
|
'required' => true,
|
||||||
|
'description' => __('E-mail addresses can be separated by commas.'),
|
||||||
|
'form_group_class' => 'col-sm-6',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'subject' => [
|
||||||
|
'text',
|
||||||
|
[
|
||||||
|
'label' => __('Message Subject'),
|
||||||
|
'belongsTo' => 'config',
|
||||||
|
'required' => true,
|
||||||
|
'description' => __(
|
||||||
|
'Variables are in the form of <code>{{ var.name }}</code>. All values in the <a href="%s" target="_blank">Now Playing API response</a> are avaliable for use. Any empty fields are ignored.',
|
||||||
|
$router->named('api:nowplaying:index')
|
||||||
|
),
|
||||||
|
'form_group_class' => 'col-sm-6',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'message' => [
|
||||||
|
'textarea',
|
||||||
|
[
|
||||||
|
'label' => __('Message Body'),
|
||||||
|
'belongsTo' => 'config',
|
||||||
|
'required' => true,
|
||||||
|
'description' => __(
|
||||||
|
'Variables are in the form of <code>{{ var.name }}</code>. All values in the <a href="%s" target="_blank">Now Playing API response</a> are avaliable for use. Any empty fields are ignored.',
|
||||||
|
$router->named('api:nowplaying:index')
|
||||||
|
),
|
||||||
|
'form_group_class' => 'col-sm-12',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'submit_grp' => [
|
||||||
|
'elements' => [
|
||||||
|
|
||||||
|
'submit' => [
|
||||||
|
'submit',
|
||||||
|
[
|
||||||
|
'type' => 'submit',
|
||||||
|
'label' => __('Save Changes'),
|
||||||
|
'class' => 'ui-button btn-lg btn-primary',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
|
@ -13,6 +13,11 @@ return [
|
||||||
'name' => __('Generic Web Hook'),
|
'name' => __('Generic Web Hook'),
|
||||||
'description' => __('Automatically send a message to any URL when your station data changes.'),
|
'description' => __('Automatically send a message to any URL when your station data changes.'),
|
||||||
],
|
],
|
||||||
|
Connector\Email::NAME => [
|
||||||
|
'class' => Connector\Email::class,
|
||||||
|
'name' => __('Send E-mail'),
|
||||||
|
'description' => __('Send an e-mail to specified address(es).'),
|
||||||
|
],
|
||||||
Connector\TuneIn::NAME => [
|
Connector\TuneIn::NAME => [
|
||||||
'class' => Connector\TuneIn::class,
|
'class' => Connector\TuneIn::class,
|
||||||
'name' => __('TuneIn AIR'),
|
'name' => __('TuneIn AIR'),
|
||||||
|
|
|
@ -7,28 +7,24 @@ use App\Exception\RateLimitExceededException;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
use App\RateLimit;
|
use App\RateLimit;
|
||||||
|
use App\Service\Mail;
|
||||||
use App\Session\Flash;
|
use App\Session\Flash;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Symfony\Component\Mailer\MailerInterface;
|
|
||||||
use Symfony\Component\Mime\Address;
|
|
||||||
use Symfony\Component\Mime\Email;
|
|
||||||
|
|
||||||
class ForgotPasswordAction
|
class ForgotPasswordAction
|
||||||
{
|
{
|
||||||
public function __invoke(
|
public function __invoke(
|
||||||
ServerRequest $request,
|
ServerRequest $request,
|
||||||
Response $response,
|
Response $response,
|
||||||
Entity\Repository\SettingsRepository $settingsRepo,
|
|
||||||
Entity\Repository\UserRepository $userRepo,
|
Entity\Repository\UserRepository $userRepo,
|
||||||
Entity\Repository\UserLoginTokenRepository $loginTokenRepo,
|
Entity\Repository\UserLoginTokenRepository $loginTokenRepo,
|
||||||
RateLimit $rateLimit,
|
RateLimit $rateLimit,
|
||||||
MailerInterface $mailer
|
Mail $mail
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$flash = $request->getFlash();
|
$flash = $request->getFlash();
|
||||||
$view = $request->getView();
|
$view = $request->getView();
|
||||||
|
|
||||||
$settings = $settingsRepo->readSettings();
|
if (!$mail->isEnabled()) {
|
||||||
if (!$settings->getMailEnabled()) {
|
|
||||||
return $view->renderToResponse($response, 'frontend/account/forgot_disabled');
|
return $view->renderToResponse($response, 'frontend/account/forgot_disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,8 +51,7 @@ class ForgotPasswordAction
|
||||||
$user = $userRepo->findByEmail($email);
|
$user = $userRepo->findByEmail($email);
|
||||||
|
|
||||||
if ($user instanceof Entity\User) {
|
if ($user instanceof Entity\User) {
|
||||||
$email = new Email();
|
$email = $mail->createMessage();
|
||||||
$email->from(new Address($settings->getMailSenderEmail(), $settings->getMailSenderName()));
|
|
||||||
$email->to($user->getEmail());
|
$email->to($user->getEmail());
|
||||||
|
|
||||||
$email->subject(__('Account Recovery Link'));
|
$email->subject(__('Account Recovery Link'));
|
||||||
|
@ -71,7 +66,7 @@ class ForgotPasswordAction
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$mailer->send($email);
|
$mail->send($email);
|
||||||
}
|
}
|
||||||
|
|
||||||
$flash->addMessage(
|
$flash->addMessage(
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Entity;
|
||||||
|
use Symfony\Component\Mailer\Envelope;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mime\Address;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
use Symfony\Component\Mime\RawMessage;
|
||||||
|
|
||||||
|
class Mail implements MailerInterface
|
||||||
|
{
|
||||||
|
protected Entity\Repository\SettingsRepository $settingsRepo;
|
||||||
|
|
||||||
|
protected MailerInterface $mailer;
|
||||||
|
|
||||||
|
public function __construct(Entity\Repository\SettingsRepository $settingsRepo, MailerInterface $mailer)
|
||||||
|
{
|
||||||
|
$this->settingsRepo = $settingsRepo;
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEnabled(): bool
|
||||||
|
{
|
||||||
|
$settings = $this->settingsRepo->readSettings();
|
||||||
|
return $settings->getMailEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createMessage(): Email
|
||||||
|
{
|
||||||
|
$settings = $this->settingsRepo->readSettings();
|
||||||
|
|
||||||
|
$email = new Email();
|
||||||
|
$email->from(new Address($settings->getMailSenderEmail(), $settings->getMailSenderName()));
|
||||||
|
|
||||||
|
return $email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(RawMessage $message, Envelope $envelope = null): void
|
||||||
|
{
|
||||||
|
$this->mailer->send($message, $envelope);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Webhook\Connector;
|
||||||
|
|
||||||
|
use App\Entity;
|
||||||
|
use App\Service\Mail;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Monolog\Logger;
|
||||||
|
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||||
|
|
||||||
|
class Email extends AbstractConnector
|
||||||
|
{
|
||||||
|
public const NAME = 'email';
|
||||||
|
|
||||||
|
protected Mail $mail;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
Logger $logger,
|
||||||
|
Client $httpClient,
|
||||||
|
Mail $mail
|
||||||
|
) {
|
||||||
|
parent::__construct($logger, $httpClient);
|
||||||
|
|
||||||
|
$this->mail = $mail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispatch(
|
||||||
|
Entity\Station $station,
|
||||||
|
Entity\StationWebhook $webhook,
|
||||||
|
Entity\Api\NowPlaying $np,
|
||||||
|
array $triggers,
|
||||||
|
bool $isStandalone
|
||||||
|
): bool {
|
||||||
|
if (!$this->mail->isEnabled()) {
|
||||||
|
$this->logger->error('E-mail delivery is not currently enabled. Skipping webhook delivery...');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = $webhook->getConfig();
|
||||||
|
$emailTo = $config['to'];
|
||||||
|
$emailSubject = $config['subject'];
|
||||||
|
$emailBody = $config['message'];
|
||||||
|
|
||||||
|
if (empty($emailTo) || empty($emailSubject) || empty($emailBody)) {
|
||||||
|
$this->logger->error('Webhook ' . self::NAME . ' is missing necessary configuration. Skipping...');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$email = $this->mail->createMessage();
|
||||||
|
|
||||||
|
$emailToParts = explode(',', $emailTo);
|
||||||
|
foreach ($emailToParts as $emailToPart) {
|
||||||
|
$email->addTo(trim($emailToPart));
|
||||||
|
}
|
||||||
|
|
||||||
|
$vars = [
|
||||||
|
'subject' => $emailSubject,
|
||||||
|
'body' => $emailBody,
|
||||||
|
];
|
||||||
|
$vars = $this->replaceVariables($vars, $np);
|
||||||
|
|
||||||
|
$email->subject($vars['subject']);
|
||||||
|
$email->text($vars['body']);
|
||||||
|
|
||||||
|
$this->mail->send($email);
|
||||||
|
} catch (TransportExceptionInterface $e) {
|
||||||
|
$this->logger->error(sprintf('Error from e-mail (%d): %s', $e->getCode(), $e->getMessage()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue