2018-08-08 09:52:53 +02:00
|
|
|
<?php
|
|
|
|
/**
|
2018-08-08 09:56:03 +02:00
|
|
|
* todo: implement caching by saving all options in one cache variable on register_shutdown_function.
|
2018-08-08 09:52:53 +02:00
|
|
|
*/
|
2018-08-08 09:56:03 +02:00
|
|
|
|
2018-08-08 09:52:53 +02:00
|
|
|
namespace App;
|
|
|
|
|
2018-09-16 12:52:52 +02:00
|
|
|
use App\Conversation;
|
2018-09-10 11:50:53 +02:00
|
|
|
use App\Notifications\BroadcastNotification;
|
2018-09-07 15:08:34 +02:00
|
|
|
use App\Notifications\WebsiteNotification;
|
2018-08-08 09:52:53 +02:00
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
|
|
|
|
class Subscription extends Model
|
|
|
|
{
|
|
|
|
// Event types
|
2018-08-09 10:14:48 +02:00
|
|
|
// Changing ticket status does not fire event
|
2018-08-08 09:56:03 +02:00
|
|
|
const EVENT_TYPE_NEW = 1;
|
|
|
|
const EVENT_TYPE_ASSIGNED = 2;
|
|
|
|
const EVENT_TYPE_UPDATED = 3;
|
2018-08-08 09:52:53 +02:00
|
|
|
const EVENT_TYPE_CUSTOMER_REPLIED = 4;
|
2018-08-08 09:56:03 +02:00
|
|
|
const EVENT_TYPE_USER_REPLIED = 5;
|
2018-09-15 15:37:29 +02:00
|
|
|
const EVENT_TYPE_USER_ADDED_NOTE = 6;
|
2018-08-08 09:52:53 +02:00
|
|
|
|
2018-09-15 15:37:29 +02:00
|
|
|
// Events
|
2018-08-08 09:52:53 +02:00
|
|
|
// Notify me when…
|
|
|
|
const EVENT_NEW_CONVERSATION = 1;
|
|
|
|
const EVENT_CONVERSATION_ASSIGNED_TO_ME = 2;
|
|
|
|
const EVENT_CONVERSATION_ASSIGNED = 6;
|
|
|
|
const EVENT_FOLLOWED_CONVERSATION_UPDATED = 13;
|
|
|
|
const EVENT_I_AM_MENTIONED = 14;
|
|
|
|
const EVENT_MY_TEAM_MENTIONED = 15;
|
|
|
|
// Notify me when a customer replies…
|
|
|
|
const EVENT_CUSTOMER_REPLIED_TO_UNASSIGNED = 4;
|
|
|
|
const EVENT_CUSTOMER_REPLIED_TO_MY = 3;
|
|
|
|
const EVENT_CUSTOMER_REPLIED_TO_ASSIGNED = 7;
|
|
|
|
// Notify me when another Help Scout user replies or adds a note…
|
|
|
|
const EVENT_USER_REPLIED_TO_UNASSIGNED = 8;
|
|
|
|
const EVENT_USER_REPLIED_TO_MY = 5;
|
|
|
|
const EVENT_USER_REPLIED_TO_ASSIGNED = 9;
|
|
|
|
|
|
|
|
// Mediums
|
2018-09-07 15:08:34 +02:00
|
|
|
const MEDIUM_EMAIL = 1; // This is also website notifications
|
2018-09-10 11:50:53 +02:00
|
|
|
const MEDIUM_BROWSER = 2; // Browser push notification
|
2018-08-08 09:52:53 +02:00
|
|
|
const MEDIUM_MOBILE = 3;
|
|
|
|
|
|
|
|
public static $mediums = [
|
|
|
|
self::MEDIUM_EMAIL,
|
|
|
|
self::MEDIUM_BROWSER,
|
|
|
|
self::MEDIUM_MOBILE,
|
|
|
|
];
|
|
|
|
|
|
|
|
public static $default_subscriptions = [
|
|
|
|
self::MEDIUM_EMAIL => [
|
|
|
|
self::EVENT_CONVERSATION_ASSIGNED_TO_ME,
|
|
|
|
self::EVENT_FOLLOWED_CONVERSATION_UPDATED,
|
|
|
|
self::EVENT_I_AM_MENTIONED,
|
|
|
|
self::EVENT_MY_TEAM_MENTIONED,
|
|
|
|
self::EVENT_CUSTOMER_REPLIED_TO_MY,
|
|
|
|
self::EVENT_USER_REPLIED_TO_MY,
|
|
|
|
],
|
|
|
|
self::MEDIUM_BROWSER => [
|
|
|
|
self::EVENT_CONVERSATION_ASSIGNED_TO_ME,
|
|
|
|
self::EVENT_FOLLOWED_CONVERSATION_UPDATED,
|
|
|
|
self::EVENT_I_AM_MENTIONED,
|
|
|
|
self::EVENT_MY_TEAM_MENTIONED,
|
|
|
|
self::EVENT_CUSTOMER_REPLIED_TO_MY,
|
|
|
|
self::EVENT_USER_REPLIED_TO_MY,
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
2018-08-08 09:56:03 +02:00
|
|
|
* List of events that occured.
|
2018-08-08 09:52:53 +02:00
|
|
|
*/
|
|
|
|
public static $occured_events = [];
|
|
|
|
|
|
|
|
public $timestamps = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The attributes that are not mass assignable.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $guarded = ['id'];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Subscribed user.
|
|
|
|
*/
|
|
|
|
public function user()
|
|
|
|
{
|
|
|
|
return $this->belongsTo('App\User');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add default subscriptions for user.
|
2018-08-08 09:56:03 +02:00
|
|
|
*
|
|
|
|
* @param int $user_id
|
2018-08-08 09:52:53 +02:00
|
|
|
*/
|
|
|
|
public static function addDefaultSubscriptions($user_id)
|
|
|
|
{
|
|
|
|
self::saveFromArray(self::$default_subscriptions, $user_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save subscriptions from passed array.
|
2018-08-08 09:56:03 +02:00
|
|
|
*
|
|
|
|
* @param array $subscriptions [description]
|
|
|
|
*
|
|
|
|
* @return [type] [description]
|
2018-08-08 09:52:53 +02:00
|
|
|
*/
|
|
|
|
public static function saveFromArray($new_subscriptions, $user_id)
|
|
|
|
{
|
|
|
|
$subscriptions = [];
|
|
|
|
|
|
|
|
if (is_array($new_subscriptions)) {
|
|
|
|
foreach ($new_subscriptions as $medium => $events) {
|
|
|
|
foreach ($events as $event) {
|
|
|
|
$subscriptions[] = [
|
|
|
|
'user_id' => $user_id,
|
|
|
|
'medium' => $medium,
|
|
|
|
'event' => $event,
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-08 09:56:03 +02:00
|
|
|
self::where('user_id', $user_id)->delete();
|
|
|
|
self::insert($subscriptions);
|
2018-08-08 09:52:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-08 09:56:03 +02:00
|
|
|
* Check if subscription exists.
|
2018-08-08 09:52:53 +02:00
|
|
|
*/
|
|
|
|
public static function exists(array $params, $subscriptions = null)
|
|
|
|
{
|
|
|
|
if ($subscriptions) {
|
|
|
|
// Look in the passed list
|
|
|
|
foreach ($subscriptions as $subscription) {
|
|
|
|
foreach ($params as $param_name => $param_value) {
|
|
|
|
if ($subscription->$param_name != $param_value) {
|
|
|
|
continue 2;
|
|
|
|
}
|
|
|
|
}
|
2018-08-08 09:56:03 +02:00
|
|
|
|
2018-08-08 09:52:53 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Search in DB
|
|
|
|
}
|
2018-08-08 09:56:03 +02:00
|
|
|
|
2018-08-08 09:52:53 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-21 20:55:32 +02:00
|
|
|
* Detect users to notify by medium.
|
2018-08-08 09:52:53 +02:00
|
|
|
*/
|
|
|
|
public static function usersToNotify($event_type, $conversation, $threads, $mailbox_user_ids = null)
|
|
|
|
{
|
|
|
|
if ($conversation->imported) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect events
|
|
|
|
$events = [];
|
|
|
|
|
|
|
|
$thread = $threads[0];
|
|
|
|
$prev_thread = null;
|
|
|
|
if (!empty($threads[1])) {
|
|
|
|
$prev_thread = $threads[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($event_type) {
|
|
|
|
case self::EVENT_TYPE_NEW:
|
|
|
|
$events[] = self::EVENT_NEW_CONVERSATION;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case self::EVENT_TYPE_ASSIGNED:
|
|
|
|
$events[] = self::EVENT_CONVERSATION_ASSIGNED_TO_ME;
|
|
|
|
$events[] = self::EVENT_CONVERSATION_ASSIGNED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case self::EVENT_TYPE_CUSTOMER_REPLIED:
|
|
|
|
$events[] = self::EVENT_FOLLOWED_CONVERSATION_UPDATED;
|
|
|
|
if (!empty($prev_thread) && $prev_thread->user_id) {
|
|
|
|
$events[] = self::EVENT_CUSTOMER_REPLIED_TO_MY;
|
|
|
|
$events[] = self::EVENT_CUSTOMER_REPLIED_TO_ASSIGNED;
|
|
|
|
} else {
|
|
|
|
$events[] = self::EVENT_CUSTOMER_REPLIED_TO_UNASSIGNED;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case self::EVENT_TYPE_USER_REPLIED:
|
2018-09-15 15:37:29 +02:00
|
|
|
case self::EVENT_TYPE_USER_ADDED_NOTE:
|
2018-08-08 09:52:53 +02:00
|
|
|
$events[] = self::EVENT_FOLLOWED_CONVERSATION_UPDATED;
|
|
|
|
if (!empty($prev_thread) && $prev_thread->user_id) {
|
|
|
|
$events[] = self::EVENT_USER_REPLIED_TO_MY;
|
|
|
|
$events[] = self::EVENT_USER_REPLIED_TO_ASSIGNED;
|
|
|
|
} else {
|
|
|
|
$events[] = self::EVENT_USER_REPLIED_TO_UNASSIGNED;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
// todo: EVENT_I_AM_MENTIONED, EVENT_MY_TEAM_MENTIONED
|
|
|
|
}
|
|
|
|
// Check if assigned user changed
|
|
|
|
$user_changed = false;
|
|
|
|
if ($event_type != self::EVENT_TYPE_ASSIGNED && $event_type != self::EVENT_TYPE_NEW) {
|
|
|
|
if ($thread->type == Thread::TYPE_LINEITEM && $thread->action_type == Thread::ACTION_TYPE_USER_CHANGED) {
|
|
|
|
$user_changed = true;
|
|
|
|
} elseif ($prev_thread) {
|
|
|
|
if ($prev_thread->user_id != $thread->user_id) {
|
|
|
|
$user_changed = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Get prev thread
|
|
|
|
if ($prev_thread && $prev_thread->user_id != $thread->user_id) {
|
|
|
|
$user_changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($user_changed) {
|
|
|
|
$events[] = self::EVENT_CONVERSATION_ASSIGNED_TO_ME;
|
|
|
|
$events[] = self::EVENT_CONVERSATION_ASSIGNED;
|
|
|
|
$events[] = self::EVENT_FOLLOWED_CONVERSATION_UPDATED;
|
|
|
|
}
|
|
|
|
$events = array_unique($events);
|
|
|
|
|
|
|
|
// Detect subscribed users
|
|
|
|
if (!$mailbox_user_ids) {
|
|
|
|
$mailbox_user_ids = $conversation->mailbox->userIdsHavingAccess();
|
|
|
|
}
|
|
|
|
|
2018-08-08 09:56:03 +02:00
|
|
|
$subscriptions = self::select(['user_id', 'medium'])
|
2018-08-08 09:52:53 +02:00
|
|
|
->whereIn('user_id', $mailbox_user_ids)
|
|
|
|
->whereIn('event', $events)
|
|
|
|
->get();
|
|
|
|
|
|
|
|
// Filter subscribers
|
|
|
|
$users_to_notify = [];
|
|
|
|
foreach ($subscriptions as $i => $subscription) {
|
|
|
|
if (in_array($subscription->event, [self::EVENT_CONVERSATION_ASSIGNED_TO_ME, self::EVENT_CUSTOMER_REPLIED_TO_MY, self::EVENT_USER_REPLIED_TO_MY]) && $conversation->user_id != $subscription->user_id
|
|
|
|
) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$users_to_notify[$subscription->medium][] = $subscription->user;
|
|
|
|
$users_to_notify[$subscription->medium] = array_unique($users_to_notify[$subscription->medium]);
|
|
|
|
}
|
2018-08-08 09:56:03 +02:00
|
|
|
|
2018-08-08 09:52:53 +02:00
|
|
|
return $users_to_notify;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Process events which occured.
|
|
|
|
*/
|
|
|
|
public static function processEvents()
|
|
|
|
{
|
|
|
|
$notify = [];
|
|
|
|
|
2018-08-21 20:55:32 +02:00
|
|
|
// Collection into notify array information about all users who need to be notified
|
2018-08-08 09:52:53 +02:00
|
|
|
foreach (self::$occured_events as $event) {
|
|
|
|
// Get mailbox users ids
|
|
|
|
$mailbox_user_ids = [];
|
|
|
|
foreach (self::$mediums as $medium) {
|
|
|
|
if (!empty($notify[$medium])) {
|
|
|
|
foreach ($notify[$medium] as $conversation_id => $notify_info) {
|
|
|
|
if ($notify_info['conversation']->mailbox_id == $event['conversation']->mailbox_id) {
|
|
|
|
$mailbox_user_ids = $notify_info['mailbox_user_ids'];
|
|
|
|
break 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-21 20:55:32 +02:00
|
|
|
// Get users and threads from previous results to avoid repeated SQL queries.
|
2018-08-08 09:52:53 +02:00
|
|
|
$users = [];
|
2018-08-21 20:55:32 +02:00
|
|
|
$threads = [];
|
2018-08-08 09:52:53 +02:00
|
|
|
foreach (self::$mediums as $medium) {
|
|
|
|
if (empty($notify[$medium][$event['conversation']->id])) {
|
2018-08-21 20:55:32 +02:00
|
|
|
$threads = $event['conversation']->getThreads();
|
2018-08-08 09:52:53 +02:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
$users = $notify[$medium][$event['conversation']->id]['users'];
|
2018-08-21 20:55:32 +02:00
|
|
|
$threads = $notify[$medium][$event['conversation']->id]['threads'];
|
2018-08-08 09:52:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-21 20:55:32 +02:00
|
|
|
$users_to_notify = self::usersToNotify($event['event_type'], $event['conversation'], $threads, $mailbox_user_ids);
|
2018-08-08 09:52:53 +02:00
|
|
|
|
|
|
|
if (!$users_to_notify) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-08-22 09:26:42 +02:00
|
|
|
|
2018-08-08 09:52:53 +02:00
|
|
|
foreach ($users_to_notify as $medium => $medium_users_to_notify) {
|
|
|
|
|
|
|
|
// Remove current user from recipients if action caused by current user
|
|
|
|
foreach ($medium_users_to_notify as $i => $user) {
|
|
|
|
if ($user->id == $event['caused_by_user_id']) {
|
2018-08-09 10:14:48 +02:00
|
|
|
unset($medium_users_to_notify[$i]);
|
2018-08-08 09:52:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($medium_users_to_notify)) {
|
|
|
|
$notify[$medium][$event['conversation']->id] = [
|
2018-08-21 20:55:32 +02:00
|
|
|
// Users subarray contains all users who need to receive notification for all events
|
2018-08-08 09:52:53 +02:00
|
|
|
'users' => array_unique(array_merge($users, $medium_users_to_notify)),
|
|
|
|
'conversation' => $event['conversation'],
|
2018-08-21 20:55:32 +02:00
|
|
|
'threads' => $threads,
|
2018-08-08 09:52:53 +02:00
|
|
|
'mailbox_user_ids' => $mailbox_user_ids,
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-07 15:08:34 +02:00
|
|
|
// Notify by email and on the website
|
2018-08-08 09:52:53 +02:00
|
|
|
if (!empty($notify[self::MEDIUM_EMAIL])) {
|
2018-09-07 15:08:34 +02:00
|
|
|
// Email notification (better to create them first)
|
2018-08-08 09:52:53 +02:00
|
|
|
foreach ($notify[self::MEDIUM_EMAIL] as $notify_info) {
|
2018-09-16 12:52:52 +02:00
|
|
|
\App\Jobs\SendNotificationToUsers::dispatch($notify_info['users'], $notify_info['conversation'], $notify_info['threads'])
|
|
|
|
->delay(now()->addSeconds(Conversation::UNDO_TIMOUT))
|
|
|
|
->onQueue('emails');
|
2018-08-08 09:52:53 +02:00
|
|
|
}
|
2018-09-10 11:50:53 +02:00
|
|
|
// - DB Notification
|
2018-09-07 15:08:34 +02:00
|
|
|
foreach ($notify[self::MEDIUM_EMAIL] as $notify_info) {
|
|
|
|
\Notification::send($notify_info['users'], new WebsiteNotification($notify_info['conversation'], $notify_info['threads'][0]));
|
2018-09-10 11:50:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send broadcast notifications:
|
|
|
|
// - Real-time notification in the menu
|
|
|
|
// - Browser push notification
|
|
|
|
$broadcasts = [];
|
|
|
|
foreach ([self::MEDIUM_EMAIL, self::MEDIUM_BROWSER] as $medium) {
|
|
|
|
if (empty($notify[$medium])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
foreach ($notify[$medium] as $notify_info) {
|
|
|
|
|
|
|
|
$thread_id = $notify_info['threads'][0]->id;
|
2018-09-08 20:02:01 +02:00
|
|
|
|
|
|
|
foreach ($notify_info['users'] as $user) {
|
2018-09-10 11:50:53 +02:00
|
|
|
$mediums = [$medium];
|
|
|
|
if (!empty($broadcasts[$notify_info['threads'][0]->id]['mediums'])) {
|
|
|
|
$mediums = array_unique(array_merge($mediums, $broadcasts[$thread_id]['mediums']));
|
|
|
|
}
|
|
|
|
$broadcasts[$thread_id] = [
|
|
|
|
'user' => $user,
|
|
|
|
'conversation' => $notify_info['conversation'],
|
|
|
|
'threads' => $notify_info['threads'],
|
|
|
|
'mediums' => $mediums,
|
|
|
|
];
|
2018-09-08 20:02:01 +02:00
|
|
|
}
|
2018-09-07 15:08:34 +02:00
|
|
|
}
|
2018-08-08 09:52:53 +02:00
|
|
|
}
|
2018-09-10 11:50:53 +02:00
|
|
|
// \Notification::sendNow($notify_info['users'], new BroadcastNotification($notify_info['conversation'], $notify_info['threads'][0]));
|
|
|
|
foreach ($broadcasts as $thread_id => $to_broadcast) {
|
|
|
|
$to_broadcast['user']->notify(new BroadcastNotification($to_broadcast['conversation'], $to_broadcast['threads'][0], $to_broadcast['mediums']));
|
|
|
|
}
|
2018-09-07 15:08:34 +02:00
|
|
|
|
2018-08-08 09:52:53 +02:00
|
|
|
// todo: mobile notification
|
2018-09-07 15:08:34 +02:00
|
|
|
|
2018-08-21 20:55:32 +02:00
|
|
|
self::$occured_events = [];
|
2018-08-08 09:52:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remember event type to process in ProcessSubscriptionEvents middleware on terminate.
|
|
|
|
*/
|
|
|
|
public static function registerEvent($event_type, $conversation, $caused_by_user_id, $process_now = false)
|
|
|
|
{
|
|
|
|
self::$occured_events[] = [
|
|
|
|
'event_type' => $event_type,
|
|
|
|
'conversation' => $conversation,
|
|
|
|
'caused_by_user_id' => $caused_by_user_id,
|
|
|
|
];
|
|
|
|
|
|
|
|
// Automatically add EVENT_TYPE_UPDATED
|
|
|
|
if (!in_array($event_type, [self::EVENT_TYPE_UPDATED, self::EVENT_TYPE_NEW])) {
|
|
|
|
self::$occured_events[] = [
|
|
|
|
'event_type' => self::EVENT_TYPE_UPDATED,
|
|
|
|
'conversation' => $conversation,
|
|
|
|
'caused_by_user_id' => $caused_by_user_id,
|
|
|
|
];
|
|
|
|
}
|
|
|
|
if ($process_now) {
|
|
|
|
self::processEvents();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|