mirror of
https://github.com/freescout-helpdesk/freescout.git
synced 2024-11-24 19:33:07 +01:00
Logging send errors
This commit is contained in:
parent
372ab66809
commit
0b9c93e00e
@ -59,7 +59,9 @@ class SendNotificationToUsers implements ShouldQueue
|
||||
$headers['In-Reply-To'] = '<'.$prev_message_id.'>';
|
||||
$headers['References'] = '<'.$prev_message_id.'>';
|
||||
|
||||
$all_failures = [];
|
||||
// We throw an exception if any of the send attempts throws an exception (connection error, etc)
|
||||
$global_exception = null;
|
||||
|
||||
foreach ($this->users as $user) {
|
||||
$message_id = \App\Mail\Mail::MESSAGE_ID_PREFIX_NOTIFICATION.'-'.$last_thread->id.'-'.$user->id.'-'.time().'@'.$mailbox->getEmailDomain();
|
||||
$headers['Message-ID'] = $message_id;
|
||||
@ -77,9 +79,29 @@ class SendNotificationToUsers implements ShouldQueue
|
||||
}
|
||||
$from = ['address' => $mailbox->email, 'name' => $from_name];
|
||||
|
||||
$exception = null;
|
||||
|
||||
try {
|
||||
Mail::to([['name' => $user->getFullName(), 'email' => $user->email]])
|
||||
->send(new UserNotification($user, $this->conversation, $this->threads, $headers, $from));
|
||||
} catch (\Exception $e) {
|
||||
// We come here in case SMTP server unavailable for example
|
||||
activity()
|
||||
->causedBy($user)
|
||||
->withProperties([
|
||||
'error' => $e->getMessage().'; File: '.$e->getFile().' ('.$e->getLine().')',
|
||||
])
|
||||
->useLog(\App\ActivityLog::NAME_EMAILS_SENDING)
|
||||
->log(\App\ActivityLog::DESCRIPTION_EMAILS_SENDING_ERROR_TO_USER);
|
||||
|
||||
$exception = $e;
|
||||
$global_exception = $e;
|
||||
}
|
||||
|
||||
if ($exception) {
|
||||
$status = SendLog::STATUS_SEND_ERROR;
|
||||
$status_message = $exception->getMessage();
|
||||
} else {
|
||||
$failures = Mail::failures();
|
||||
|
||||
// Save to send log
|
||||
@ -88,32 +110,35 @@ class SendNotificationToUsers implements ShouldQueue
|
||||
} else {
|
||||
$status = SendLog::STATUS_ACCEPTED;
|
||||
}
|
||||
SendLog::log($last_thread->id, $message_id, $user->email, $status, null, $user->id);
|
||||
|
||||
$all_failures = array_merge($all_failures, $failures);
|
||||
}
|
||||
if (!empty($all_failures)) {
|
||||
throw new \Exception('Could not send email to: '.implode(', ', $all_failures));
|
||||
|
||||
SendLog::log($last_thread->id, $message_id, $user->email, $status, null, $user->id, $status_message);
|
||||
}
|
||||
|
||||
if ($global_exception) {
|
||||
throw $global_exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The job failed to process.
|
||||
* This method is called after attempts had finished.
|
||||
* At this stage method has access only to variables passed in constructor.
|
||||
*
|
||||
* @param Exception $exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function failed(\Exception $e)
|
||||
{
|
||||
// Write to activity log
|
||||
activity()
|
||||
//->causedBy($this->customer)
|
||||
->withProperties([
|
||||
'error' => $e->getMessage().'; File: '.$e->getFile().' ('.$e->getLine().')',
|
||||
//'to' => $this->customer->getMainEmail(),
|
||||
])
|
||||
->useLog(\App\ActivityLog::NAME_EMAILS_SENDING)
|
||||
->log(\App\ActivityLog::DESCRIPTION_EMAILS_SENDING_ERROR_TO_USER);
|
||||
}
|
||||
// public function failed(\Exception $e)
|
||||
// {
|
||||
// // Write to activity log
|
||||
// activity()
|
||||
// //->causedBy($this->customer)
|
||||
// ->withProperties([
|
||||
// 'error' => $e->getMessage().'; File: '.$e->getFile().' ('.$e->getLine().')',
|
||||
// //'to' => $this->customer->getMainEmail(),
|
||||
// ])
|
||||
// ->useLog(\App\ActivityLog::NAME_EMAILS_SENDING)
|
||||
// ->log(\App\ActivityLog::DESCRIPTION_EMAILS_SENDING_ERROR_TO_USER);
|
||||
// }
|
||||
}
|
||||
|
@ -93,17 +93,21 @@ class SendReplyToCustomer implements ShouldQueue
|
||||
->bcc($bcc_array)
|
||||
->send(new ReplyToCustomer($this->conversation, $this->threads, $headers));
|
||||
} catch (\Exception $e) {
|
||||
// We come here in case SMTP server unavailable for example
|
||||
activity()
|
||||
//->causedBy()
|
||||
->causedBy($this->customer)
|
||||
->withProperties([
|
||||
'error' => $e->getMessage().'; File: '.$e->getFile().' ('.$e->getLine().')',
|
||||
])
|
||||
->useLog(\App\ActivityLog::NAME_EMAILS_SENDING)
|
||||
->log(\App\ActivityLog::DESCRIPTION_EMAILS_SENDING_ERROR_TO_CUSTOMER);
|
||||
|
||||
// Failures will be save to send log when retry attempts will finish
|
||||
// Failures will be saved to send log when retry attempts will finish
|
||||
$this->failures = $this->recipients;
|
||||
|
||||
// Save to send log
|
||||
$this->saveToSendLog($e->getMessage());
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@ -117,51 +121,50 @@ class SendReplyToCustomer implements ShouldQueue
|
||||
|
||||
// Save to send log
|
||||
$this->saveToSendLog();
|
||||
|
||||
if (!empty($failures)) {
|
||||
throw new \Exception('Could not send email to: '.implode(', ', $failures));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The job failed to process.
|
||||
* This method is called after number of attempts had finished.
|
||||
* This method is called after attempts had finished.
|
||||
* At this stage method has access only to variables passed in constructor.
|
||||
*
|
||||
* @param Exception $exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function failed(\Exception $e)
|
||||
{
|
||||
activity()
|
||||
->causedBy($this->customer)
|
||||
->withProperties([
|
||||
'error' => $e->getMessage().'; File: '.$e->getFile().' ('.$e->getLine().')',
|
||||
'to' => $this->customer->getMainEmail(),
|
||||
])
|
||||
->useLog(\App\ActivityLog::NAME_EMAILS_SENDING)
|
||||
->log(\App\ActivityLog::DESCRIPTION_EMAILS_SENDING_ERROR_TO_CUSTOMER);
|
||||
// public function failed(\Exception $e)
|
||||
// {
|
||||
// activity()
|
||||
// ->causedBy($this->customer)
|
||||
// ->withProperties([
|
||||
// 'error' => $e->getMessage().'; File: '.$e->getFile().' ('.$e->getLine().')',
|
||||
// 'to' => $this->customer->getMainEmail(),
|
||||
// ])
|
||||
// ->useLog(\App\ActivityLog::NAME_EMAILS_SENDING)
|
||||
// ->log(\App\ActivityLog::DESCRIPTION_EMAILS_SENDING_ERROR_TO_CUSTOMER);
|
||||
|
||||
$this->saveToSendLog();
|
||||
}
|
||||
// $this->saveToSendLog();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Save failed email to send log.
|
||||
* Save emails to send log.
|
||||
*/
|
||||
public function saveToSendLog()
|
||||
public function saveToSendLog($error_message = '')
|
||||
{
|
||||
foreach ($this->recipients as $recipient) {
|
||||
if (in_array($recipient, $this->failures)) {
|
||||
$status = SendLog::STATUS_SEND_ERROR;
|
||||
$status_message = $error_message;
|
||||
} else {
|
||||
$status = SendLog::STATUS_ACCEPTED;
|
||||
$status_message = '';
|
||||
}
|
||||
if ($this->customer_email == $recipient) {
|
||||
$customer_id = $this->customer->id;
|
||||
} else {
|
||||
$customer_id = null;
|
||||
}
|
||||
SendLog::log($this->last_thread->id, $this->message_id, $recipient, $status, $customer_id);
|
||||
SendLog::log($this->last_thread->id, $this->message_id, $recipient, $status, $customer_id, null, $status_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class SendNotificationToUsers
|
||||
// Detect event type by event class
|
||||
switch (get_class($event)) {
|
||||
case 'App\Events\UserReplied':
|
||||
$caused_by_user_id = $event->conversation->created_by_user_id;
|
||||
$caused_by_user_id = $event->thread->created_by_user_id;
|
||||
$event_type = Subscription::EVENT_TYPE_USER_REPLIED;
|
||||
break;
|
||||
case 'App\Events\UserCreatedConversation':
|
||||
|
@ -281,6 +281,7 @@ class Subscription extends Model
|
||||
if (!$users_to_notify) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($users_to_notify as $medium => $medium_users_to_notify) {
|
||||
|
||||
// Remove current user from recipients if action caused by current user
|
||||
@ -291,17 +292,6 @@ class Subscription extends Model
|
||||
}
|
||||
|
||||
if (count($medium_users_to_notify)) {
|
||||
|
||||
// Remove duplicate users
|
||||
// $user_ids = [];
|
||||
// foreach ($users as $i => $user) {
|
||||
// if (in_array($user->id, $user_ids)) {
|
||||
// unset($users[$i]);
|
||||
// } else {
|
||||
// $user_ids[] = $user->id;
|
||||
// }
|
||||
// }
|
||||
|
||||
$notify[$medium][$event['conversation']->id] = [
|
||||
// Users subarray contains all users who need to receive notification for all events
|
||||
'users' => array_unique(array_merge($users, $medium_users_to_notify)),
|
||||
|
@ -156,12 +156,12 @@ return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Parameters used to run queued jobs processing.
|
||||
| Check for new jobs every 5 seconds.
|
||||
| Retry failed jobs max 7 times (for example no connection to the mail server)
|
||||
| Tries happen every minute.
|
||||
| Checks for new jobs every 5 seconds.
|
||||
| Do not set more than 1 retry, as it may lead to sending repeated emails if one recipient fails
|
||||
| and another succeeds.
|
||||
|-------------------------------------------------------------------------
|
||||
*/
|
||||
'queue_work_params' => ['--queue' => 'emails', '--sleep' => '5', '--tries' => '7'],
|
||||
'queue_work_params' => ['--queue' => 'emails', '--sleep' => '5', '--tries' => '1'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
5
public/js/main.js
vendored
5
public/js/main.js
vendored
@ -732,8 +732,13 @@ function showModal(a, onshow)
|
||||
var modal_class = a.attr('data-modal-class');
|
||||
// Fit modal body into the screen
|
||||
var fit = a.attr('data-modal-fit');
|
||||
var size = a.attr('data-modal-size'); // lg or sm
|
||||
var modal;
|
||||
|
||||
if (size) {
|
||||
modal_class += ' modal-'+size;
|
||||
}
|
||||
|
||||
var html = [
|
||||
'<div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="jsmodal-label" aria-hidden="true">',
|
||||
'<div class="modal-dialog '+modal_class+'">',
|
||||
|
@ -1,5 +1,5 @@
|
||||
@if (!$customers_log && !$users_log)
|
||||
<div class="alert alert-warning">{{ __("Log is empty") }}</div>
|
||||
<div class="alert alert-warning">{{ __("There were no send attempts yet") }}</div>
|
||||
@else
|
||||
@if (!empty($customers_log))
|
||||
<h5>{{ __("Emails to customers") }}</h5>
|
||||
@ -16,7 +16,7 @@
|
||||
@if ($loop->index == 0)
|
||||
<td rowspan="{{ count($logs) }}">{{ $email }}</td>
|
||||
@endif
|
||||
<td class="small">{{ App\User::dateFormat($log->created_at) }}</td>
|
||||
<td>{{ App\User::dateFormat($log->created_at, 'M j, Y H:i:s') }}</td>
|
||||
<td>
|
||||
<span class="@if ($log->isErrorStatus())text-danger @elseif ($log->isSuccessStatus()) text-success @endif">{{ $log->getStatusName() }}</span>
|
||||
@if ($log->status_message)
|
||||
@ -44,7 +44,7 @@
|
||||
@if ($loop->index == 0)
|
||||
<td rowspan="{{ count($logs) }}">{{ $email }}</td>
|
||||
@endif
|
||||
<td class="small">{{ App\User::dateFormat($log->created_at) }}</td>
|
||||
<td>{{ App\User::dateFormat($log->created_at) }}</td>
|
||||
<td>
|
||||
<span class="@if ($log->isErrorStatus())text-danger @elseif ($log->isSuccessStatus()) text-success @endif">{{ $log->getStatusName() }}</span>
|
||||
@if ($log->status_message)
|
||||
|
@ -188,7 +188,7 @@
|
||||
@if (Auth::user()->isAdmin())
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||
<li><a href="{{ route('conversations.ajax_html', ['action' =>
|
||||
'send_log']) }}?thread_id={{ $thread->id }}" title="{{ __("View outgoing emails") }}" data-trigger="modal" data-modal-title="{{ __("Outgoing Emails") }}">{{ __("Outgoing Emails") }}</a></li>
|
||||
'send_log']) }}?thread_id={{ $thread->id }}" title="{{ __("View outgoing emails") }}" data-trigger="modal" data-modal-title="{{ __("Outgoing Emails") }}" data-modal-size="lg">{{ __("Outgoing Emails") }}</a></li>
|
||||
</ul>
|
||||
@endif
|
||||
</div>
|
||||
@ -293,11 +293,13 @@
|
||||
<li><a href="#" title="" class="thread-edit-trigger">{{ __("Edit") }} (todo)</a></li>
|
||||
<li><a href="javascript:alert('todo: implement hiding threads');void(0);" title="" class="thread-hide-trigger">{{ __("Hide") }} (todo)</a></li>
|
||||
<li><a href="javascript:alert('todo: implement creating new conversation from thread');void(0);" title="{{ __("Start a conversation from this thread") }}" class="new-conv">{{ __("New Conversation") }}</a></li>
|
||||
@if ($thread->headers)
|
||||
<li><a href="{{ route('conversations.ajax_html', ['action' =>
|
||||
'show_original']) }}?thread_id={{ $thread->id }}" title="{{ __("Show original message headers") }}" data-trigger="modal" data-modal-title="{{ __("Original Message Headers") }}" data-modal-fit="true">{{ __("Show Original") }}</a></li>
|
||||
'show_original']) }}?thread_id={{ $thread->id }}" title="{{ __("Show original message headers") }}" data-trigger="modal" data-modal-title="{{ __("Original Message Headers") }}" data-modal-fit="true" data-modal-size="lg">{{ __("Show Original") }}</a></li>
|
||||
@endif
|
||||
@if (Auth::user()->isAdmin())
|
||||
<li><a href="{{ route('conversations.ajax_html', ['action' =>
|
||||
'send_log']) }}?thread_id={{ $thread->id }}" title="{{ __("View outgoing emails") }}" data-trigger="modal" data-modal-title="{{ __("Outgoing Emails") }}">{{ __("Outgoing Emails") }}</a></li>
|
||||
'send_log']) }}?thread_id={{ $thread->id }}" title="{{ __("View outgoing emails") }}" data-trigger="modal" data-modal-title="{{ __("Outgoing Emails") }}" data-modal-size="lg">{{ __("Outgoing Emails") }}</a></li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -34,10 +34,12 @@
|
||||
@foreach ($logs as $row)
|
||||
<tr>
|
||||
@foreach ($cols as $col)
|
||||
<td>
|
||||
<td class="break-words">
|
||||
@if (isset($row[$col]))
|
||||
@if ($col == 'user' || $col == 'customer')
|
||||
<a href="{{ $row[$col]->url() }}">{{ $row[$col]->getFullName(true) }}</a>
|
||||
@elseif ($col == 'date')
|
||||
{{ App\User::dateFormat(new Illuminate\Support\Carbon($row[$col]), 'M j, H:i:s') }}
|
||||
@else
|
||||
{{ $row[$col] }}
|
||||
@endif
|
||||
|
Loading…
Reference in New Issue
Block a user