PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.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.10.0, at app/Hooks/Handlers/EmailNotificationHandler.php

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