diff --git a/app/Events/Email/InvalidEmailDetected.php b/app/Events/Email/InvalidEmailDetected.php
new file mode 100644
index 000000000..d8ba13657
--- /dev/null
+++ b/app/Events/Email/InvalidEmailDetected.php
@@ -0,0 +1,51 @@
+email = $email;
+
+ $this->error = $error;
+
+ $this->setContact();
+
+ $this->setUser();
+ }
+
+ public function setContact()
+ {
+ $contact = Contact::email($this->email)->enabled()->first();
+
+ if (empty($contact)) {
+ return;
+ }
+
+ $this->contact = $contact;
+ }
+
+ public function setUser()
+ {
+ $user = User::email($this->email)->enabled()->first();
+
+ if (empty($user)) {
+ return;
+ }
+
+ $this->user = $user;
+ }
+}
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index 7878ebe75..0bb396a0b 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -3,6 +3,7 @@
namespace App\Exceptions;
use Akaunting\Money\Exceptions\UnexpectedAmountException;
+use App\Events\Email\InvalidEmailDetected;
use App\Exceptions\Http\Resource as ResourceException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@@ -15,6 +16,7 @@ use Illuminate\View\ViewException;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Mailer\Exception\HttpTransportException as MailerHttpTransportException;
use Throwable;
class Handler extends ExceptionHandler
@@ -194,6 +196,21 @@ class Handler extends ExceptionHandler
}
}
+ if ($exception instanceof MailerHttpTransportException) {
+ /**
+ * Couldn't access the SentMessage object to get the email address
+ * https://symfony.com/doc/current/mailer.html#debugging-emails
+ *
+ * https://codespeedy.com/extract-email-addresses-from-a-string-in-php
+ * https://phpliveregex.com/p/IMG
+ */
+ preg_match("/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}/", $exception->getMessage(), $matches);
+
+ if (! empty($matches[0])) {
+ event(new InvalidEmailDetected($matches[0], $exception->getMessage()));
+ }
+ }
+
return parent::render($request, $exception);
}
diff --git a/app/Listeners/Email/DisablePersonDueToInvalidEmail.php b/app/Listeners/Email/DisablePersonDueToInvalidEmail.php
new file mode 100644
index 000000000..de20c3a82
--- /dev/null
+++ b/app/Listeners/Email/DisablePersonDueToInvalidEmail.php
@@ -0,0 +1,35 @@
+disableContact($event);
+
+ $this->disableUser($event);
+ }
+
+ public function disableContact(Event $event): void
+ {
+ if (empty($event->contact)) {
+ return;
+ }
+
+ $event->contact->enabled = false;
+ $event->contact->save();
+ }
+
+ public function disableUser(Event $event): void
+ {
+ if (empty($event->user)) {
+ return;
+ }
+
+ $event->user->enabled = false;
+ $event->user->save();
+ }
+}
diff --git a/app/Listeners/Email/SendInvalidEmailNotification.php b/app/Listeners/Email/SendInvalidEmailNotification.php
new file mode 100644
index 000000000..d5812fce6
--- /dev/null
+++ b/app/Listeners/Email/SendInvalidEmailNotification.php
@@ -0,0 +1,61 @@
+users;
+
+ $this->notifyAdminsAboutInvalidContactEmail($event, $users);
+
+ $this->notifyAdminsAboutInvalidUserEmail($event, $users);
+ }
+
+ public function notifyAdminsAboutInvalidContactEmail(Event $event, $users): void
+ {
+ if (empty($event->contact)) {
+ return;
+ }
+
+ if ($event->contact->isCustomer() || $event->contact->isVendor() || $event->contact->isEmployee()) {
+ $type = trans('general.' . Str::plural($event->contact->type), 1);
+ } else {
+ $type = ucfirst($event->contact->type);
+ }
+
+ foreach ($users as $user) {
+ if ($user->cannot('read-notifications')) {
+ continue;
+ }
+
+ $user->notify(new InvalidEmail($event->email, $type, $event->error));
+ }
+ }
+
+ public function notifyAdminsAboutInvalidUserEmail(Event $event, $users): void
+ {
+ if (empty($event->user)) {
+ return;
+ }
+
+ $type = trans('general.users', 1);
+
+ foreach ($users as $user) {
+ if ($user->cannot('read-notifications')) {
+ continue;
+ }
+
+ if ($user->email == $event->email) {
+ continue;
+ }
+
+ $user->notify(new InvalidEmail($event->email, $type, $event->error));
+ }
+ }
+}
diff --git a/app/Models/Auth/User.php b/app/Models/Auth/User.php
index 0ca68e85d..b1b679489 100644
--- a/app/Models/Auth/User.php
+++ b/app/Models/Auth/User.php
@@ -244,6 +244,11 @@ class User extends Authenticatable implements HasLocalePreference
return $query->wherePermissionIs('read-admin-panel');
}
+ public function scopeEmail($query, $email)
+ {
+ return $query->where('email', '=', $email);
+ }
+
/**
* Attach company_ids attribute to model.
*
diff --git a/app/Notifications/Email/InvalidEmail.php b/app/Notifications/Email/InvalidEmail.php
new file mode 100644
index 000000000..f458c4f15
--- /dev/null
+++ b/app/Notifications/Email/InvalidEmail.php
@@ -0,0 +1,76 @@
+email = $email;
+
+ $this->type = $type;
+
+ $this->error = $error;
+
+ $this->onQueue('notifications');
+ }
+
+ /**
+ * Get the notification's channels.
+ *
+ * @param mixed $notifiable
+ * @return array|string
+ */
+ public function via($notifiable)
+ {
+ return ['mail', 'database'];
+ }
+
+ /**
+ * Build the mail representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ public function toMail($notifiable)
+ {
+ $dashboard_url = route('dashboard', ['company_id' => company_id()]);
+
+ return (new MailMessage)
+ ->subject(trans('notifications.email.invalid.title', ['type' => $this->type]))
+ ->line(trans('notifications.email.invalid.description', ['email' => $this->email]))
+ ->line($this->error)
+ ->action(trans_choice('general.dashboards', 1), $dashboard_url);
+ }
+
+ /**
+ * Get the array representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function toArray($notifiable)
+ {
+ return [
+ 'title' => trans('notifications.menu.invalid_email.title', ['type' => $this->type]),
+ 'description' => trans('notifications.menu.invalid_email.description', ['email' => $this->email]),
+ 'email' => $this->email,
+ ];
+ }
+}
diff --git a/app/Providers/Event.php b/app/Providers/Event.php
index 1f3a6b808..1049d0268 100644
--- a/app/Providers/Event.php
+++ b/app/Providers/Event.php
@@ -108,6 +108,10 @@ class Event extends Provider
'App\Listeners\Email\ReportTooManyEmailsSent',
'App\Listeners\Email\TellFirewallTooManyEmailsSent',
],
+ 'App\Events\Email\InvalidEmailDetected' => [
+ 'App\Listeners\Email\DisablePersonDueToInvalidEmail',
+ 'App\Listeners\Email\SendInvalidEmailNotification',
+ ],
];
/**
diff --git a/app/Traits/Contacts.php b/app/Traits/Contacts.php
index a529b6b6b..68bdf90fb 100644
--- a/app/Traits/Contacts.php
+++ b/app/Traits/Contacts.php
@@ -18,6 +18,13 @@ trait Contacts
return in_array($type, $this->getVendorTypes());
}
+ public function isEmployee()
+ {
+ $type = $this->type ?? $this->contact->type ?? $this->model->type ?? 'employee';
+
+ return in_array($type, $this->getEmployeeTypes());
+ }
+
public function getCustomerTypes($return = 'array')
{
return $this->getContactTypes('customer', $return);
@@ -28,6 +35,11 @@ trait Contacts
return $this->getContactTypes('vendor', $return);
}
+ public function getEmployeeTypes($return = 'array')
+ {
+ return $this->getContactTypes('employee', $return);
+ }
+
public function getContactTypes($index, $return = 'array')
{
$types = (string) setting('contact.type.' . $index);
@@ -45,6 +57,11 @@ trait Contacts
$this->addContactType($new_type, 'vendor');
}
+ public function addEmployeeType($new_type)
+ {
+ $this->addContactType($new_type, 'employee');
+ }
+
public function addContactType($new_type, $index)
{
$types = explode(',', setting('contact.type.' . $index));
diff --git a/config/setting.php b/config/setting.php
index 2741bcdaf..79267ff23 100644
--- a/config/setting.php
+++ b/config/setting.php
@@ -171,6 +171,7 @@ return [
'type' => [
'customer' => env('SETTING_FALLBACK_CONTACT_TYPE_CUSTOMER', Contact::CUSTOMER_TYPE),
'vendor' => env('SETTING_FALLBACK_CONTACT_TYPE_VENDOR', Contact::VENDOR_TYPE),
+ 'employee' => env('SETTING_FALLBACK_CONTACT_TYPE_EMPLOYEE', Contact::EMPLOYEE_TYPE),
],
],
'transaction' => [
diff --git a/resources/lang/en-GB/general.php b/resources/lang/en-GB/general.php
index 849f76b98..373f92d87 100644
--- a/resources/lang/en-GB/general.php
+++ b/resources/lang/en-GB/general.php
@@ -72,6 +72,7 @@ return [
'attachments' => 'Attachment|Attachments',
'histories' => 'History|Histories',
'your_notifications' => 'Your notification|Your notifications',
+ 'employees' => 'Employee|Employees',
'welcome' => 'Welcome',
'banking' => 'Banking',
diff --git a/resources/lang/en-GB/notifications.php b/resources/lang/en-GB/notifications.php
index 79cac3a8d..b9013cdad 100644
--- a/resources/lang/en-GB/notifications.php
+++ b/resources/lang/en-GB/notifications.php
@@ -63,6 +63,17 @@ return [
],
+ 'email' => [
+
+ 'invalid' => [
+
+ 'title' => 'Invalid :type Email',
+ 'description' => ':email email address has been reported as invalid and the person has been disabled. Please check the following error message and fix the email address:',
+
+ ],
+
+ ],
+
'menu' => [
'export_completed' => [
@@ -177,6 +188,13 @@ return [
],
+ 'invalid_email' => [
+
+ 'title' => 'Invalid :type Email',
+ 'description' => ':email email address has been reported as invalid and the person has been disabled. Please check and fix the email address.',
+
+ ],
+
],
'messages' => [