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
← All changes | app/Http/Controllers/ProfileController.php +134 -41 2.7.72.10.0 View file →
@@ -4,13 +4,11 @@
4 4
5 5 use FluentCommunity\App\Functions\Utility;
6 6 use FluentCommunity\App\Models\Comment;
7 7 use FluentCommunity\App\Models\Feed;
8 -use FluentCommunity\App\Models\NotificationSubscription;
9 8 use FluentCommunity\App\Models\Space;
10 9 use FluentCommunity\App\Models\SpaceGroup;
11 10 use FluentCommunity\App\Models\SpaceUserPivot;
12 -use FluentCommunity\App\Models\User;
13 11 use FluentCommunity\App\Models\XProfile;
14 12 use FluentCommunity\App\Services\CustomSanitizer;
15 13 use FluentCommunity\App\Services\FeedsHelper;
16 14 use FluentCommunity\App\Services\Helper;
@@ -20,8 +18,10 @@
20 18 use FluentCommunity\Framework\Support\Arr;
21 19 use FluentCommunity\Modules\Course\Model\CourseLesson;
22 20 use FluentCommunity\Modules\Course\Model\CourseTopic;
23 21 use FluentCommunity\Modules\Course\Services\CourseHelper;
22 +use FluentCommunity\Modules\PushNotification\PushNotificationModule;
23 +use FluentCommunity\Framework\Foundation\Exceptions\HttpException;
24 24
25 25 class ProfileController extends Controller
26 26 {
27 27 public function getProfile(Request $request, $userName)
@@ -75,9 +75,9 @@
75 75 $isAdmin = Helper::isSiteAdmin($currentUserId);
76 76
77 77 if ($isOwn || $isAdmin) {
78 78 $enableUserSync = Utility::getPrivacySetting('enable_user_sync') === 'yes';
79 - $nameArray = explode(' ', trim($xprofile->display_name));
79 + $nameArray = explode(' ', trim((string) $xprofile->display_name));
80 80 $xprofileFirstName = array_shift($nameArray);
81 81 $xprofileLastName = implode(' ', $nameArray);
82 82
83 83 $profile['email'] = $user->user_email;
@@ -156,9 +156,9 @@
156 156 }
157 157
158 158 public function patchProfile(Request $request, $userName)
159 159 {
160 - $xprofile = $this->verfifyAndGetProfile($userName);
160 + $xprofile = $this->verifyAndGetProfile($userName);
161 161
162 162 $updateData = $request->get('data', []);
163 163
164 164 if (!empty($updateData['status']) && $updateData['status'] === 'deactivated' && $xprofile->status === 'active') {
@@ -205,9 +205,9 @@
205 205
206 206 if (isset($updateData['avatar'])) {
207 207
208 208 if ($xprofile->hasCustomAvatar()) {
209 - $deletedMedias[] = $xprofile->attributes['avatar'];
209 + $deletedMedias[] = Arr::get($xprofile->getAttributes(), 'avatar');
210 210 }
211 211
212 212 $xprofile->avatar = $updateData['avatar'];
213 213
@@ -312,10 +312,13 @@
312 312 $userNameChanged = $userName != $xProfile->username;
313 313 }
314 314
315 315 if (Helper::isFeatureEnabled('user_badge')) {
316 - $badgeSlug = (array)Arr::get($data, 'badge_slugs', []);
317 - $meta['badge_slug'] = $badgeSlug;
316 + $badgeSlug = array_filter((array) Arr::get($data, 'badge_slugs', []), 'is_scalar');
317 + $badgeSlug = array_map('sanitize_text_field', $badgeSlug);
318 +
319 + $definedBadges = (array) Utility::getOption('user_badges', []);
320 + $meta['badge_slug'] = array_values(array_intersect($badgeSlug, array_keys($definedBadges)));
318 321 }
319 322 } else if (Utility::getPrivacySetting('can_customize_username')) {
320 323 $userName = Arr::get($data, 'username');
321 324
@@ -352,10 +355,10 @@
352 355 }
353 356
354 357 $updateData['display_name'] = trim(sanitize_text_field(Arr::get($data, 'first_name') . ' ' . Arr::get($data, 'last_name')));
355 358
356 - $updateData['short_description'] = CustomSanitizer::unslashMarkdown(sanitize_textarea_field(trim(Arr::get($data, 'short_description'))));
357 - $meta['website'] = sanitize_url(Arr::get($data, 'website'));
359 + $updateData['short_description'] = CustomSanitizer::unslashMarkdown(sanitize_textarea_field(trim((string) Arr::get($data, 'short_description', ''))));
360 + $meta['website'] = sanitize_url((string) Arr::get($data, 'website', ''));
358 361 $meta['headline'] = sanitize_text_field(trim(Arr::get($data, 'headline', '')));
359 362 $socialLinks = Arr::get($data, 'social_links', []);
360 363
361 364 $maxDescriptionLength = apply_filters('fluent_community/max_profile_description_length', 5000);
@@ -550,12 +553,19 @@
550 553 'permission_failed' => true
551 554 ]);
552 555 }
553 556
554 - $memberships = $xProfile->space_pivot()
555 - ->where('status', 'active')
556 - ->pluck('space_id');
557 + $canSeeSecret = $xProfile->user_id == get_current_user_id()
558 + || ($currentUser && $currentUser->isCommunityModerator());
557 559
560 + $memberships = $xProfile->spaces()
561 + ->wherePivot('status', 'active')
562 + ->when(!$canSeeSecret, function ($q) {
563 + $q->whereIn('privacy', ['public', 'private']);
564 + })
565 + ->get()
566 + ->pluck('id');
567 +
558 568 return apply_filters('fluent_community/profile_all_memberships_api_response', [
559 569 'memberships' => $memberships
560 570 ], $request->all());
561 571 }
@@ -710,47 +720,51 @@
710 720 public function getNotificationPreferance(Request $request, $userName)
711 721 {
712 722 $emailPref = Utility::getEmailNotificationSettings();
713 723
714 - $xProfile = $this->verfifyAndGetProfile($userName);
724 + $xProfile = $this->verifyAndGetProfile($userName);
715 725
716 726 $globalPreferances = NotificationPref::getGlobalPrefs();
717 727
718 - $userPrefs = NotificationSubscription::where('user_id', $xProfile->user_id)
719 - ->select(['notification_type', 'is_read', 'object_id'])
720 - ->get();
728 + // Read through the same service the save path writes through. These rows
729 + // live in fcom_notification_prefs, keyed by flat keys - space-scoped ones
730 + // carry an '_<space id>' suffix.
731 + $userPrefs = NotificationPref::getUserPrefs($xProfile->user_id);
721 732
733 + $frequencyMaps = [
734 + 0 => 'disabled',
735 + 1 => 'hourly',
736 + 2 => 'daily',
737 + 3 => 'weekly'
738 + ];
739 +
722 740 $userGlobalPrefs = [];
723 741 $spaceWisePrefs = [];
724 - foreach ($userPrefs as $pref) {
725 - if (!$pref->object_id) {
726 - if ($pref->notification_type === 'message_email_frequency') {
727 - $maps = [
728 - 0 => 'disabled',
729 - 1 => 'hourly',
730 - 2 => 'daily',
731 - 3 => 'weekly'
732 - ];
742 + foreach ($userPrefs as $prefKey => $prefValue) {
743 + if ($prefKey === 'message_email_frequency') {
744 + $userGlobalPrefs[$prefKey] = isset($frequencyMaps[$prefValue]) ? $frequencyMaps[$prefValue] : 'default';
745 + continue;
746 + }
733 747
734 - if (isset($maps[$pref->is_read])) {
735 - $userGlobalPrefs[$pref->notification_type] = $maps[$pref->is_read];
736 - } else {
737 - $userGlobalPrefs[$pref->notification_type] = 'default';
738 - }
739 - continue;
748 + if (preg_match('/^(np_by_(?:member|admin)_mail)_(\d+)$/', $prefKey, $matches)) {
749 + $spaceId = (int)$matches[2];
750 +
751 + if (empty($spaceWisePrefs[$spaceId])) {
752 + $spaceWisePrefs[$spaceId] = [];
740 753 }
741 - $userGlobalPrefs[$pref->notification_type] = $pref->is_read ? 'yes' : 'no';
742 - } else {
743 - if (empty($spaceWisePrefs[$pref->object_id])) {
744 - $spaceWisePrefs[$pref->object_id] = [];
745 - }
746 - $spaceWisePrefs[$pref->object_id][$pref->notification_type] = $pref->is_read;
754 +
755 + $spaceWisePrefs[$spaceId][$matches[1]] = $prefValue;
756 + continue;
747 757 }
758 +
759 + $userGlobalPrefs[$prefKey] = $prefValue ? 'yes' : 'no';
748 760 }
749 761
750 762 $messagingConfig = Utility::getOption('_messaging_settings', []);
751 763 $isGlobalPerUser = Arr::get($messagingConfig, 'messaging_email_frequency') == 'disabled';
752 764
765 + $pushAvailable = PushNotificationModule::isAvailable();
766 +
753 767 $userGlobalPrefsDefaults = [
754 768 'digest_mail' => Arr::get($globalPreferances, 'digest_email_status') ? 'yes' : 'no',
755 769 'mention_mail' => Arr::get($globalPreferances, 'mention_mail') ? 'yes' : 'no',
756 770 'reply_my_com_mail' => Arr::get($globalPreferances, 'reply_my_com_mail') ? 'yes' : 'no',
@@ -757,8 +771,17 @@
757 771 'com_my_post_mail' => Arr::get($globalPreferances, 'com_my_post_mail') ? 'yes' : 'no',
758 772 'message_email_frequency' => $isGlobalPerUser ? 'disabled' : 'default'
759 773 ];
760 774
775 + if ($pushAvailable) {
776 + $pushPreferances = NotificationPref::getGlobalPrefs('push');
777 +
778 + $userGlobalPrefsDefaults['com_my_post_push'] = Arr::get($pushPreferances, 'com_my_post_push') ? 'yes' : 'no';
779 + $userGlobalPrefsDefaults['reply_my_com_push'] = Arr::get($pushPreferances, 'reply_my_com_push') ? 'yes' : 'no';
780 + $userGlobalPrefsDefaults['mention_push'] = Arr::get($pushPreferances, 'mention_push') ? 'yes' : 'no';
781 + $userGlobalPrefsDefaults['co_com_push'] = Arr::get($pushPreferances, 'co_com_push') ? 'yes' : 'no';
782 + }
783 +
761 784 $userGlobalPrefs = wp_parse_args($userGlobalPrefs, $userGlobalPrefsDefaults);
762 785
763 786 $profileUserId = $xProfile->user_id;
764 787 $spaceGroups = SpaceGroup::with(['spaces' => function ($query) use ($profileUserId) {
@@ -856,8 +879,16 @@
856 879 $digestDay = $maps[$digestDay];
857 880 }
858 881 }
859 882
883 + $crmEmailStatus = '';
884 + if ($xProfile->user_id == get_current_user_id()) {
885 + $profileUser = get_user_by('ID', $xProfile->user_id);
886 + if ($profileUser && $profileUser->user_email) {
887 + $crmEmailStatus = Helper::getCrmUndeliverableStatus($profileUser->user_email);
888 + }
889 + }
890 +
860 891 $data = [
861 892 'user_globals' => (object)$userGlobalPrefs,
862 893 'spaceGroups' => $formattedSpaceGroups,
863 894 'space_prefs' => $spaceWisePrefs,
@@ -862,8 +893,10 @@
862 893 'spaceGroups' => $formattedSpaceGroups,
863 894 'space_prefs' => $spaceWisePrefs,
864 895 'digestEmailDay' => $digestDay,
865 896 'default_messaging_email_frequency' => Arr::get($messagingConfig, 'messaging_email_status') !== 'yes' ? 'no' : Arr::get($messagingConfig, 'messaging_email_frequency'),
897 + 'crm_email_status' => $crmEmailStatus,
898 + 'push_available' => $pushAvailable,
866 899 ];
867 900
868 901 return apply_filters('fluent_community/profile_notification_pref_api_response', $data, $request->all());
869 902 }
@@ -869,9 +902,9 @@
869 902 }
870 903
871 904 public function saveNotificationPreferance(Request $request, $userName)
872 905 {
873 - $xProfile = $this->verfifyAndGetProfile($userName);
906 + $xProfile = $this->verifyAndGetOwnProfile($userName);
874 907
875 908 $userPrefs = $request->get('user_globals', []);
876 909 $sapcePrefs = $request->get('space_prefs', []);
877 910
@@ -914,15 +947,75 @@
914 947 'message' => __('Email Notification preferences have been updated', 'fluent-community')
915 948 ];
916 949 }
917 950
918 - private function verfifyAndGetProfile($userName)
951 + public function reconfirmEmail(Request $request, $userName)
919 952 {
953 + if (!defined('FLUENTCRM')) {
954 + return $this->sendError([
955 + 'message' => __('FluentCRM is not available on this site', 'fluent-community')
956 + ]);
957 + }
958 +
920 959 $xProfile = XProfile::where('username', $userName)->firstOrFail();
921 960
922 - $currentUser = $this->getUser();
923 - if ($xProfile->user_id != get_current_user_id() && (!$currentUser || !$currentUser->isCommunityModerator())) {
961 + if ($xProfile->user_id != get_current_user_id()) {
962 + return $this->sendError([
963 + 'message' => __('You can only re-confirm your own email address', 'fluent-community')
964 + ]);
965 + }
966 +
967 + $profileUser = get_user_by('ID', $xProfile->user_id);
968 + $email = $profileUser ? $profileUser->user_email : '';
969 +
970 + if (!$email || !Helper::getCrmUndeliverableStatus($email)) {
971 + return $this->sendError([
972 + 'message' => __('Your email address does not need re-confirmation', 'fluent-community')
973 + ]);
974 + }
975 +
976 + $subscriber = \FluentCrm\App\Models\Subscriber::where('email', $email)->first();
977 +
978 + if (!$subscriber) {
979 + return $this->sendError([
980 + 'message' => __('Your email address does not need re-confirmation', 'fluent-community')
981 + ]);
982 + }
983 +
984 + // In-memory only, never saved: the opt-in sender is gated on status == 'pending'
985 + // and does not persist the subscriber, so the stored status stays untouched
986 + // and FluentCommunity keeps pausing emails until the confirmation link is clicked.
987 + $subscriber->status = 'pending';
988 +
989 + if (!$subscriber->sendDoubleOptinEmail()) {
990 + return $this->sendError([
991 + 'message' => __('The confirmation email could not be sent right now. Please try again after a few minutes.', 'fluent-community')
992 + ]);
993 + }
994 +
995 + return [
996 + 'message' => __('A confirmation email has been sent. Please check your inbox and click the confirmation link to resume email notifications.', 'fluent-community')
997 + ];
998 + }
999 +
1000 + private function verifyAndGetProfile($userName)
1001 + {
1002 + $xProfile = XProfile::where('username', $userName)->firstOrFail();
1003 +
1004 + $currentUserId = get_current_user_id();
1005 + if ($xProfile->user_id != $currentUserId && !Helper::isSuperAdmin($currentUserId)) {
924 1006 throw new \Exception('You are not allowed to update this profile');
1007 + }
1008 +
1009 + return $xProfile;
1010 + }
1011 +
1012 + private function verifyAndGetOwnProfile($userName)
1013 + {
1014 + $xProfile = XProfile::where('username', $userName)->firstOrFail();
1015 +
1016 + if (!get_current_user_id() || $xProfile->user_id != get_current_user_id()) {
1017 + throw new HttpException(403, esc_html__('You are not allowed to access these notification preferences.', 'fluent-community'));
925 1018 }
926 1019
927 1020 return $xProfile;
928 1021 }