PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.7.5
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.7.5
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
fluent-community / app / Hooks / Handlers / EmailNotificationHandler.php

EmailNotificationHandler.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.7.5, at app/Hooks/Handlers/EmailNotificationHandler.php

711 lines 26.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\App\Hooks\Handlers;
4
5 use FluentCommunity\App\Functions\Utility;
6 use FluentCommunity\App\Models\Comment;
7 use FluentCommunity\App\Models\Feed;
8 use FluentCommunity\App\Models\Notification;
9 use FluentCommunity\App\Models\BaseSpace;
10 use FluentCommunity\App\Models\SpaceUserPivot;
11 use FluentCommunity\App\Models\User;
12 use FluentCommunity\App\Models\XProfile;
13 use FluentCommunity\App\Services\Helper;
14 use FluentCommunity\App\Services\Libs\DailyDigest;
15 use FluentCommunity\App\Services\Libs\Mailer;
16 use FluentCommunity\App\Services\FeedsHelper;
17 use FluentCommunity\App\Services\NotificationPref;
18 use FluentCommunity\App\Services\ProfileHelper;
19 use FluentCommunity\Framework\Support\Arr;
20
21 class EmailNotificationHandler
22 {
23 private $maxRunTime = 0;
24
25 public function register()
26 {
27 add_action('fluent_community/space_feed/created', [$this, 'handleSpaceFeedCreated'], 20, 1);
28 add_action('fluent_community/email_notify_new_posts', [$this, 'notifyOnPostCreatedAsync'], 10, 1);
29
30 add_action('fluent_community/comment_added', [$this, 'handleNewCommentEvent'], 30, 2);
31 add_action('fluent_community/comment_added_async', [$this, 'handleNewCommentNotificationAsync'], 10, 2);
32
33 /*
34 * This is an async request
35 */
36 add_action('fluent_community/email_notify_users_everyone_tag', [$this, 'emailNotifyUsersForEveryoneTag'], 10, 2);
37
38 add_action('fluent_community_send_daily_digest', [$this, 'maybeSendDailyDigest'], 10);
39 add_action('fluent_community/space/join_requested', [$this, 'handleCommunityJoinRequest'], 10, 2);
40
41 add_action('fluent_community/send_new_user_notification', [$this, 'sendNewUserNotificationAsync'], 10, 1);
42 }
43
44 public function sendNewUserNotificationAsync($userId)
45 {
46 $userId = (int) $userId;
47 if (!$userId || !get_userdata($userId)) {
48 return;
49 }
50 wp_new_user_notification($userId, null, 'user');
51 }
52
53 public function handleSpaceFeedCreated($feed)
54 {
55 if (did_action('fluent_community/feed/scheduling_everyone_tag')) {
56 return;
57 }
58
59 $space = $feed->space;
60 if (!$space) {
61 return false;
62 }
63
64 $types = ['np_by_member_mail'];
65 $spaceRole = $feed->user->getSpaceRole($feed->space);
66 if (in_array($spaceRole, ['admin', 'moderator'])) {
67 $types[] = 'np_by_admin_mail';
68 }
69
70 $hasSubscribers = User::query()->where(function ($query) use ($types, $space, $feed) {
71 $query->whereHas('notificationSubscriptions', function ($query) use ($types, $space) {
72 $query->whereIn('notification_type', $types)
73 ->where('object_id', $space->id)
74 ->where('is_read', 1);
75 });
76
77 do_action_ref_array('fluent_community/space_feed/email_notify_sub_query', [&$query, $feed, $space, $types]);
78
79 return $query;
80 })->exists();
81
82 if ($hasSubscribers || Arr::get($feed->meta, 'mentioned_user_ids', [])) {
83 // We are scheduling this after 2 minutes of the post publish for performance
84 as_schedule_single_action(time() + 120, 'fluent_community/email_notify_new_posts', [
85 $feed->id
86 ], 'fluent-community');
87 }
88 }
89
90 public function notifyOnPostCreatedAsync($feedId)
91 {
92 if (!$this->maxRunTime) {
93 $this->maxRunTime = Utility::getMaxRunTime();
94 }
95
96 if (is_numeric($feedId)) {
97 $feed = Feed::find($feedId);
98 } else {
99 $feed = $feedId;
100 }
101
102 if (!$feed || !$feed instanceof Feed) {
103 return;
104 }
105
106 $space = $feed->space;
107 if (!$space || !$feed->user) {
108 return;
109 }
110
111 $types = ['np_by_member_mail'];
112 $spaceRole = $feed->user->getSpaceRole($feed->space);
113 if (in_array($spaceRole, ['admin', 'moderator'])) {
114 $types[] = 'np_by_admin_mail';
115 }
116
117 $lastSendUserId = (int)$feed->getCustomMeta('_last_email_user_id', 0);
118 $usersQuery = User::query()->where(function ($query) use ($types, $space, $feed) {
119 $query->whereHas('notificationSubscriptions', function ($query) use ($types, $space) {
120 $query->whereIn('notification_type', $types)
121 ->where('object_id', $space->id)
122 ->where('is_read', 1);
123 });
124
125 $mentionedUserIds = Arr::get($feed->meta, 'mentioned_user_ids', []);
126
127 if ($mentionedUserIds) {
128 $query->orWhereIn('ID', $mentionedUserIds);
129 }
130
131 do_action_ref_array('fluent_community/space_feed/email_notify_sub_query', [&$query, $feed, $space, $types]);
132
133 return $query;
134 })
135 ->whereHas('space_pivot', function ($query) use ($space) {
136 $query->where('space_id', $space->id)
137 ->where('status', 'active');
138 })
139 ->when($lastSendUserId, function ($q) use ($lastSendUserId) {
140 $q->where('ID', '>', $lastSendUserId);
141 })
142 ->whereHas('xprofile', function ($query) {
143 return $query->where('status', 'active');
144 })
145 ->orderBy('ID', 'ASC')
146 ->limit(60);
147
148 $users = $usersQuery->get();
149
150 if ($users->isEmpty()) {
151 return; // It's done
152 }
153
154 $emailSubject = \sprintf(
155 /* translators: %1$s is the author name and %2$s is the post excerpt (max 30 chars) */
156 __('New Post By %1$s: %2$s', 'fluent-community'),
157 $feed->user->getDisplayName(),
158 $feed->getHumanExcerpt(30)
159 );
160
161 $emailBody = $feed->getFeedHtml(true);
162
163 /*
164 * must need to replace these two strings
165 * ##feed_permalink##
166 * ##email_notification_url##
167 */
168 $feedLink = $feed->getPermalink();
169
170 $startTime = microtime(true);
171 $maxSendPerSecond = 10; // max 10 emails per second
172
173 foreach ($users as $index => $user) {
174 $feed->updateCustomMeta('_last_email_user_id', $user->ID);
175 if ($user->ID == $feed->user_id) {
176 continue;
177 }
178
179 $newEmailBody = str_replace([
180 '##feed_permalink##',
181 '##email_notification_url##'
182 ], [
183 ProfileHelper::signUserUrlWithAuthHash($feedLink, $user->ID),
184 ProfileHelper::getSignedNotificationPrefUrl($user->ID)
185 ], $emailBody);
186
187 $notificationBagde = $this->getNotificationBadges($user->ID);
188 if ($notificationBagde) {
189 $newEmailBody = str_replace('<!--before_footer_section-->', $notificationBagde, $newEmailBody);
190 }
191
192 $hooksSections = apply_filters('fluent_community/new_feed_notification/email_sections', [
193 'before_content' => '',
194 'after_content' => ''
195 ], $user, $feed);
196
197 if (!empty($hooksSections['before_content'])) {
198 $newEmailBody = str_replace('<!--email_content_before-->', $hooksSections['before_content'], $newEmailBody);
199 }
200
201 if (!empty($hooksSections['after_content'])) {
202 $newEmailBody = str_replace('<!--email_content_after-->', $hooksSections['after_content'], $newEmailBody);
203 }
204
205 $mailer = new Mailer('', $emailSubject, $newEmailBody);
206 $mailer->to($user->user_email, $user->display_name);
207 $mailer->send();
208
209 if (microtime(true) - FLUENT_COMMUNITY_START_TIME > $this->maxRunTime) {
210 // It's been 45 seconds, let's stop and schedule the next one and schedule a new one
211 as_schedule_single_action(time(), 'fluent_community/email_notify_new_posts', [$feed->id], 'fluent-community');
212 return true;
213 }
214
215 if (($index + 1) % $maxSendPerSecond == 0) {
216 $timeTaken = microtime(true) - $startTime;
217 if ($timeTaken < 1) {
218 usleep((int)(1000000 - ($timeTaken * 1000000)));
219 }
220 $startTime = microtime(true);
221 }
222 }
223
224 return $this->notifyOnPostCreatedAsync($feed);
225 }
226
227 public function handleNewCommentEvent(Comment $comment, $feed)
228 {
229 // Check the comment mentioned users or not
230 if (Arr::get($comment->meta, 'mentioned_user_ids', [])) {
231 as_schedule_single_action(time(), 'fluent_community/comment_added_async', [$comment->id, 0], 'fluent-community');
232 return;
233 }
234
235 if ($comment->parent_id) {
236 $globalCommentStatus = $this->isEnabled('reply_my_com_mail');
237 } else {
238 $globalCommentStatus = $this->isEnabled('com_my_post_mail');
239 }
240
241 $authorId = FeedsHelper::getNotificationAuthorId($feed);
242
243 $notificationUserIds = [];
244 if ($comment->user_id != $authorId && NotificationPref::willGetCommentEmail($authorId, $globalCommentStatus)) {
245 as_schedule_single_action(time(), 'fluent_community/comment_added_async', [$comment->id, 0], 'fluent-community');
246 return true;
247 }
248
249 if ($comment->parent_id) {
250 $notificationUserIds = $comment->getCommentParentUserIds();
251 $notificationUserIds = array_filter($notificationUserIds, function ($userId) use ($globalCommentStatus) {
252 return NotificationPref::willGetCommentReplyEmail($userId, $globalCommentStatus);
253 });
254 }
255
256 if (!$notificationUserIds) {
257 return false;
258 }
259
260 as_schedule_single_action(time(), 'fluent_community/comment_added_async', [$comment->id, 0], 'fluent-community');
261 return true;
262 }
263
264 /*
265 * This is for the async notification for new comment
266 */
267 public function handleNewCommentNotificationAsync($commentId, $lastUserId = 0)
268 {
269 $comment = Comment::find($commentId);
270 if (!$comment) {
271 return true;
272 }
273
274 $feed = $comment->post;
275 if (!$feed) {
276 return;
277 }
278
279 if ($comment->parent_id) {
280 $globalCommentStatus = $this->isEnabled('reply_my_com_mail');
281 } else {
282 $globalCommentStatus = $this->isEnabled('com_my_post_mail');
283 }
284
285 $notificationUserIds = $comment->getCommentParentUserIds($lastUserId);
286 $notificationUserIds = array_filter($notificationUserIds, function ($userId) use ($globalCommentStatus) {
287 return NotificationPref::willGetCommentReplyEmail($userId, $globalCommentStatus);
288 });
289
290 $notificationUserIds = array_diff($notificationUserIds, [$comment->user_id]);
291 $authorId = FeedsHelper::getNotificationAuthorId($feed);
292 if ($comment->user_id != $authorId && NotificationPref::willGetCommentEmail($authorId, $globalCommentStatus)) {
293 // Add at the first
294 $notificationUserIds[] = $authorId;
295 }
296
297 // the mentioned user ids
298 if ($mentionedUserIds = Arr::get($comment->meta, 'mentioned_user_ids', [])) {
299 foreach ($mentionedUserIds as $mentionedUserId) {
300 if (NotificationPref::willGetMentionEmail($mentionedUserId, $this->isEnabled('mention_mail'))) {
301 $notificationUserIds[] = $mentionedUserId;
302 }
303 }
304 }
305
306 if (!$notificationUserIds) {
307 return;
308 }
309
310 $notificationUserIds = array_unique($notificationUserIds);
311
312 $users = User::query()->whereIn('ID', $notificationUserIds)
313 ->whereHas('xprofile', function ($query) {
314 return $query->where('status', 'active');
315 })
316 ->when($lastUserId, function ($q) use ($lastUserId) {
317 $q->where('ID', '>', $lastUserId);
318 })
319 ->orderBy('ID', 'ASC')
320 ->get();
321
322 if ($users->isEmpty()) {
323 return; // it's done
324 }
325
326 $emailBody = $comment->getCommentHtml(true);
327 $emailSubject = $comment->getEmailSubject($feed);
328
329 $feedPermalik = $feed->getPermalink() . '?comment_id=' . $comment->id;
330 $usersCount = count($users);
331
332 $startTime = microtime(true);
333 $maxSendPerSecond = 10; // max 10 emails per second
334 foreach ($users as $index => $user) {
335 $lastUserId = $user->ID;
336 if ($user->ID == $comment->user_id) {
337 continue;
338 }
339
340 $newEmailBody = str_replace([
341 '##feed_permalink##',
342 '##email_notification_url##'
343 ], [
344 ProfileHelper::signUserUrlWithAuthHash($feedPermalik, $user->ID),
345 ProfileHelper::getSignedNotificationPrefUrl($user->ID)
346 ], $emailBody);
347
348 $notificationBagde = $this->getNotificationBadges($user->ID);
349 if ($notificationBagde) {
350 $newEmailBody = str_replace('<!--before_footer_section-->', $notificationBagde, $newEmailBody);
351 }
352
353 $hooksSections = apply_filters('fluent_community/comment_notification/email_sections', [
354 'before_content' => '',
355 'after_content' => ''
356 ], $user, $comment);
357
358 if (!empty($hooksSections['before_content'])) {
359 $newEmailBody = str_replace('<!--email_content_before-->', $hooksSections['before_content'], $newEmailBody);
360 }
361
362 if (!empty($hooksSections['after_content'])) {
363 $newEmailBody = str_replace('<!--email_content_after-->', $hooksSections['after_content'], $newEmailBody);
364 }
365
366 $mailer = new Mailer('', $emailSubject, $newEmailBody);
367 $mailer->to($user->user_email, $user->display_name);
368 $mailer->send();
369
370 if (microtime(true) - FLUENT_COMMUNITY_START_TIME > 50 && $index < ($usersCount - 1)) {
371 // It's been 45 seconds, let's stop and schedule the next one and schedule a new one
372 as_schedule_single_action(time(), 'fluent_community/comment_added_async', [$comment->id, $lastUserId], 'fluent-community');
373 return true;
374 }
375
376 if (($index + 1) % $maxSendPerSecond == 0) {
377 $timeTaken = microtime(true) - $startTime;
378 if ($timeTaken < 1) {
379 usleep((int)(1000000 - ($timeTaken * 1000000)));
380 }
381 $startTime = microtime(true);
382 }
383 }
384
385 // It's done
386 return true;
387 }
388
389 public function emailNotifyUsersForEveryoneTag($feedId, $lastSendUserId = 0)
390 {
391 if (!$this->maxRunTime) {
392 $this->maxRunTime = Utility::getMaxRunTime();
393 }
394
395 // Let's try to send email to all users of this space for this post
396 $feed = Feed::find($feedId);
397
398 if (!$feed || !$feed->space) {
399 return true;
400 }
401
402 if (!$feed->isEnabledForEveryoneTag()) {
403 return true;
404 }
405
406 $notification = Notification::where('action', 'space_feed/created')
407 ->where('feed_id', $feed->id)
408 ->first();
409
410 if (!$notification) {
411 return true;
412 }
413
414 $users = User::whereDoesntHave('notificationSubscriptions', function ($query) {
415 $query->where('notification_type', 'mention_mail')
416 ->where('is_read', 0);
417 })
418 ->whereHas('space_pivot', function ($query) use ($feed) {
419 $query->where('space_id', $feed->space_id)
420 ->where('status', 'active');
421 })
422 ->whereHas('xprofile', function ($query) {
423 return $query->where('status', 'active');
424 })
425 ->orderBy('ID', 'ASC')
426 ->when($lastSendUserId, function ($q) use ($lastSendUserId) {
427 $q->where('ID', '>', $lastSendUserId);
428 })
429 ->limit(100)
430 ->get();
431
432 if ($users->isEmpty()) {
433 return true; // it's done
434 }
435
436 $author = $feed->user;
437
438 $emailSubject = \sprintf(
439 /* translators: for admin post to send email all space members: %1$s is the feed title, %2$s is the author name and %3$s space name */
440 __('%1$s - %2$s [%3$s]', 'fluent-community'),
441 $feed->getHumanExcerpt(30),
442 $author->display_name,
443 $feed->space->title
444 );
445
446 $emailBody = $feed->getFeedHtml(true);
447 $feedPermalink = $feed->getPermalink();
448
449 $startTime = microtime(true);
450 $maxSendPerSecond = 10; // max 10 emails per second
451
452 foreach ($users as $index => $user) {
453 $lastSendUserId = $user->ID;
454 if ($user->ID == $author->ID) {
455 continue;
456 }
457
458 $newEmailBody = str_replace([
459 '##feed_permalink##',
460 '##email_notification_url##'
461 ], [
462 ProfileHelper::signUserUrlWithAuthHash($feedPermalink, $user->ID),
463 ProfileHelper::getSignedNotificationPrefUrl($user->ID)
464 ], $emailBody);
465
466 $notificationBagde = $this->getNotificationBadges($user->ID);
467 if ($notificationBagde) {
468 $newEmailBody = str_replace('<!--before_footer_section-->', $notificationBagde, $newEmailBody);
469 }
470
471 $hooksSections = apply_filters('fluent_community/new_feed_everybody_notification/email_sections', [
472 'before_content' => '',
473 'after_content' => ''
474 ], $user, $feed);
475
476 if (!empty($hooksSections['before_content'])) {
477 $newEmailBody = str_replace('<!--email_content_before-->', $hooksSections['before_content'], $newEmailBody);
478 }
479
480 if (!empty($hooksSections['after_content'])) {
481 $newEmailBody = str_replace('<!--email_content_after-->', $hooksSections['after_content'], $newEmailBody);
482 }
483
484 $mailer = new Mailer('', $emailSubject, $newEmailBody);
485 $mailer->to($user->user_email, $user->display_name);
486 $mailer->send();
487
488 if (microtime(true) - FLUENT_COMMUNITY_START_TIME > $this->maxRunTime) {
489 // It's been 45 seconds, let's stop and schedule the next one and schedule a new one
490 as_schedule_single_action(time(), 'fluent_community/email_notify_users_everyone_tag', [$feedId, $lastSendUserId], 'fluent-community');
491 return true;
492 }
493
494 if (($index + 1) % $maxSendPerSecond == 0) {
495 $timeTaken = microtime(true) - $startTime;
496 if ($timeTaken < 1) {
497 usleep((int)(1000000 - ($timeTaken * 1000000)));
498 }
499 $startTime = microtime(true);
500 }
501 }
502
503 sleep(1); // sleeping for 1 second
504 return $this->emailNotifyUsersForEveryoneTag($feedId, $lastSendUserId);
505 }
506
507 public function maybeSendDailyDigest()
508 {
509 if (!$this->maxRunTime) {
510 $this->maxRunTime = Utility::getMaxRunTime();
511 }
512
513 $settings = Utility::getEmailNotificationSettings();
514
515 $digestEmailDay = Arr::get($settings, 'digest_mail_day');
516 if (strtolower(gmdate('D', current_time('timestamp'))) != $digestEmailDay) {
517 return false;
518 }
519
520 $lastSentDate = Utility::getOption('last_digest_sent_date');
521 if ($lastSentDate && gmdate('Y-m-d', strtotime($lastSentDate)) == gmdate('Y-m-d', current_time('timestamp'))) {
522 return false; // already completed
523 }
524
525 $globalEnabled = Arr::get($settings, 'digest_email_status') === 'yes';
526 $lastSentUserId = Utility::getOption('last_digest_sent_user_id');
527
528 if ($globalEnabled) {
529 $users = User::whereDoesntHave('notification_records', function ($query) {
530 $query->where('notification_type', 'digest_mail')
531 ->where('is_read', 0);
532 })
533 ->whereHas('xprofile', function ($query) {
534 $query->where('status', 'active');
535 })
536 ->when($lastSentUserId, function ($q) use ($lastSentUserId) {
537 $q->where('ID', '>', $lastSentUserId);
538 })
539 ->limit(100)
540 ->orderBy('ID', 'ASC')
541 ->get();
542 } else {
543 $users = User::whereHas('notification_records', function ($query) {
544 $query->where('notification_type', 'digest_mail')
545 ->where('is_read', 1);
546 })
547 ->whereHas('xprofile', function ($query) {
548 $query->where('status', 'active');
549 })
550 ->when($lastSentUserId, function ($q) use ($lastSentUserId) {
551 $q->where('ID', '>', $lastSentUserId);
552 })
553 ->limit(100)
554 ->orderBy('ID', 'ASC')
555 ->get();
556 }
557
558 if ($users->isEmpty()) {
559 // It's done
560 Utility::updateOption('last_digest_sent_date', current_time('mysql'));
561 Utility::updateOption('last_digest_sent_user_id', 0);
562 return false;
563 }
564
565 $startAt = microtime(true);
566 $maxSendPerSecond = 10;
567 $sentCount = 0;
568
569 foreach ($users as $user) {
570 Utility::updateOption('last_digest_sent_user_id', $user->ID);
571 $emailDigest = new DailyDigest($user);
572 if ($emailDigest->send()) {
573 $sentCount++;
574 }
575
576 if (microtime(true) - FLUENT_COMMUNITY_START_TIME > $this->maxRunTime) {
577 // It's been 45 seconds, let's stop and schedule the next one and schedule a new one
578 as_schedule_single_action(time(), 'fluent_community_send_daily_digest', [], 'fluent-community', false);
579 return true;
580 }
581
582 if ($sentCount % $maxSendPerSecond === 0) {
583 $timeTaken = microtime(true) - $startAt;
584 if ($timeTaken < 1) {
585 usleep((int)(1000000 - ($timeTaken * 1000000)));
586 }
587 $startAt = microtime(true);
588 }
589 }
590
591 return $this->maybeSendDailyDigest();
592 }
593
594 private function isEnabled($key)
595 {
596 $settings = Utility::getEmailNotificationSettings();
597
598 return Arr::get($settings, $key) === 'yes';
599 }
600
601 public function handleCommunityJoinRequest(BaseSpace $space, $userId)
602 {
603 $xProfile = XProfile::where('user_id', $userId)->first();
604 if (!$xProfile) {
605 return;
606 }
607
608 $emailComposer = new \FluentCommunity\App\Services\Libs\EmailComposer();
609
610 $emailComposer->addBlock('paragraph', __('Hi Space Leader,', 'fluent-community'));
611 /* translators: %1$s is replaced by the name of the user who requested to join the space, %2$s is replaced by the title of the space */
612 $emailComposer->addBlock('paragraph', sprintf(__('You have a new join request from %1$s to join %2$s.', 'fluent-community'), '<b>' . $xProfile->display_name . '</b>', '<b>' . $space->title . '</b>'));
613 $emailComposer->addBlock('paragraph', __('Please review the request in the portal', 'fluent-community'));
614
615 $emailComposer->addBlock('button', __('View Pending Join Requests', 'fluent-community'), [
616 'link' => Helper::baseUrl('space/' . $space->slug . '/members?view_pending=yes')
617 ]);
618
619 $emailComposer->setDefaultLogo();
620
621 /* translators: %s is replaced by the title of the space */
622 $emailComposer->addFooterLine('paragraph', sprintf(__('You are getting this email because you are an admin/moderator at %s', 'fluent-community'), '<a style="text-decoration: underline !important;" href="' . $space->getPermalink() . '">' . $space->title . '</a>'));
623
624 $emailBody = $emailComposer->getHtml();
625
626 $emailSubject = \sprintf(
627 /* translators: %1$s is replaced by the name of the user who requested to join the space, %2$s is replaced by the title of the space */
628 __('%1$s requested to join %2$s', 'fluent-community'),
629 $xProfile->display_name,
630 $space->title
631 );
632
633 $moderatorsUserIds = SpaceUserPivot::where('space_id', $space->id)
634 ->whereIn('role', ['moderator', 'admin'])
635 ->pluck('user_id')
636 ->toArray();
637
638 if (!$moderatorsUserIds) {
639 return false;
640 }
641
642 $mailer = new Mailer('', $emailSubject, $emailBody);
643 if (count($moderatorsUserIds) == 1) {
644 $modUserId = $moderatorsUserIds[0];
645
646 $moderator = get_user_by('ID', $modUserId);
647
648 if (!$moderator || !$moderator->user_email) {
649 return;
650 }
651
652 $mailer->to($moderator->user_email, $moderator->display_name);
653 return $mailer->send();
654 }
655
656 // send by BCC by 12 chunks
657 $chunks = array_chunk($moderatorsUserIds, 12);
658
659 foreach ($chunks as $chunk) {
660
661 $mailer = new Mailer('', $emailSubject, $emailBody);
662
663 $users = User::whereIn('ID', $chunk)->get();
664 $first = null;
665 foreach ($users as $user) {
666 if (!$user || !$user->user_email) {
667 continue;
668 }
669 if (!$first) {
670 $mailer->to($user->user_email, $user->display_name);
671 $first = $user;
672 continue;
673 }
674 $mailer->addBCC($user->display_name . ' <' . $user->user_email . '>');
675 }
676
677 if ($first) {
678 $mailer->send();
679 sleep(1); // sleeping for 1 second
680 }
681 }
682
683 return true;
684 }
685
686 private function getNotificationBadges($userId)
687 {
688 $unreadCount = Notification::byStatus('unread', $userId)->count();
689 $unreadMessages = apply_filters('fluent_messaging/get_unread_message_count', 0, $userId);
690
691 $html = '';
692 $linkColor = Utility::getThemeColor();
693 if ($unreadCount) {
694 $notificationUrl = ProfileHelper::signUserUrlWithAuthHash(Helper::baseUrl('notifications'), $userId);
695 /* translators: %d is replaced by the number of unread notifications */
696 $html = '<a style="text-decoration: none; color: ' . esc_attr($linkColor) . ';" href="' . $notificationUrl . '">' . sprintf(__('🔔 %d Unread Notifications', 'fluent-community'), $unreadCount) . '</a>';
697 if ($unreadMessages) {
698 $html .= '<span style="margin: 0 10px;"> | </span>';
699 }
700 }
701
702 if ($unreadMessages) {
703 $chatUrl = ProfileHelper::signUserUrlWithAuthHash(Helper::baseUrl('chat'), $userId);
704 /* translators: %d is replaced by the number of unread messages */
705 $html .= '<a style="text-decoration: none; color: ' . esc_attr($linkColor) . ';" href="' . $chatUrl . '">' . sprintf(__('✉️ %d Unread Messages', 'fluent-community'), $unreadMessages) . '</a>';
706 }
707
708 return $html;
709 }
710 }
711