PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.6.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.6.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 1.1.0 All 77 releases
fluent-community / app / Hooks / Handlers / EmailNotificationHandler.php

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

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