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

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