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 +141 -41 2.7.52.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;
@@ -85,8 +85,9 @@
85 85 $profile['last_name'] = $enableUserSync ? $user->last_name : $xprofileLastName;
86 86 $profile['short_description'] = $xprofile->short_description;
87 87 $profile['can_change_username'] = $isAdmin || Utility::getPrivacySetting('can_customize_username') === 'yes';
88 88 $profile['can_change_email'] = current_user_can('edit_users') || (Utility::getPrivacySetting('can_change_email') === 'yes' && $isOwn);
89 + $profile['can_change_password'] = $isOwn && Utility::getPrivacySetting('can_change_password') === 'yes';
89 90 }
90 91
91 92 $profileBaseUrl = Helper::baseUrl('u/' . $xprofile->username . '/');
92 93
@@ -155,9 +156,9 @@
155 156 }
156 157
157 158 public function patchProfile(Request $request, $userName)
158 159 {
159 - $xprofile = $this->verfifyAndGetProfile($userName);
160 + $xprofile = $this->verifyAndGetProfile($userName);
160 161
161 162 $updateData = $request->get('data', []);
162 163
163 164 if (!empty($updateData['status']) && $updateData['status'] === 'deactivated' && $xprofile->status === 'active') {
@@ -204,9 +205,9 @@
204 205
205 206 if (isset($updateData['avatar'])) {
206 207
207 208 if ($xprofile->hasCustomAvatar()) {
208 - $deletedMedias[] = $xprofile->attributes['avatar'];
209 + $deletedMedias[] = Arr::get($xprofile->getAttributes(), 'avatar');
209 210 }
210 211
211 212 $xprofile->avatar = $updateData['avatar'];
212 213
@@ -311,10 +312,13 @@
311 312 $userNameChanged = $userName != $xProfile->username;
312 313 }
313 314
314 315 if (Helper::isFeatureEnabled('user_badge')) {
315 - $badgeSlug = (array)Arr::get($data, 'badge_slugs', []);
316 - $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)));
317 321 }
318 322 } else if (Utility::getPrivacySetting('can_customize_username')) {
319 323 $userName = Arr::get($data, 'username');
320 324
@@ -351,10 +355,10 @@
351 355 }
352 356
353 357 $updateData['display_name'] = trim(sanitize_text_field(Arr::get($data, 'first_name') . ' ' . Arr::get($data, 'last_name')));
354 358
355 - $updateData['short_description'] = CustomSanitizer::unslashMarkdown(sanitize_textarea_field(trim(Arr::get($data, 'short_description'))));
356 - $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', ''));
357 361 $meta['headline'] = sanitize_text_field(trim(Arr::get($data, 'headline', '')));
358 362 $socialLinks = Arr::get($data, 'social_links', []);
359 363
360 364 $maxDescriptionLength = apply_filters('fluent_community/max_profile_description_length', 5000);
@@ -457,8 +461,14 @@
457 461 'message' => __('You are not allowed to change this password', 'fluent-community')
458 462 ]);
459 463 }
460 464
465 + if (Utility::getPrivacySetting('can_change_password') !== 'yes') {
466 + return $this->sendError([
467 + 'message' => __('Password change is disabled', 'fluent-community')
468 + ]);
469 + }
470 +
461 471 $data = $request->get('data', []);
462 472
463 473 $this->validate($data, [
464 474 'current_password' => 'required',
@@ -543,12 +553,19 @@
543 553 'permission_failed' => true
544 554 ]);
545 555 }
546 556
547 - $memberships = $xProfile->space_pivot()
548 - ->where('status', 'active')
549 - ->pluck('space_id');
557 + $canSeeSecret = $xProfile->user_id == get_current_user_id()
558 + || ($currentUser && $currentUser->isCommunityModerator());
550 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 +
551 568 return apply_filters('fluent_community/profile_all_memberships_api_response', [
552 569 'memberships' => $memberships
553 570 ], $request->all());
554 571 }
@@ -703,47 +720,51 @@
703 720 public function getNotificationPreferance(Request $request, $userName)
704 721 {
705 722 $emailPref = Utility::getEmailNotificationSettings();
706 723
707 - $xProfile = $this->verfifyAndGetProfile($userName);
724 + $xProfile = $this->verifyAndGetProfile($userName);
708 725
709 726 $globalPreferances = NotificationPref::getGlobalPrefs();
710 727
711 - $userPrefs = NotificationSubscription::where('user_id', $xProfile->user_id)
712 - ->select(['notification_type', 'is_read', 'object_id'])
713 - ->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);
714 732
733 + $frequencyMaps = [
734 + 0 => 'disabled',
735 + 1 => 'hourly',
736 + 2 => 'daily',
737 + 3 => 'weekly'
738 + ];
739 +
715 740 $userGlobalPrefs = [];
716 741 $spaceWisePrefs = [];
717 - foreach ($userPrefs as $pref) {
718 - if (!$pref->object_id) {
719 - if ($pref->notification_type === 'message_email_frequency') {
720 - $maps = [
721 - 0 => 'disabled',
722 - 1 => 'hourly',
723 - 2 => 'daily',
724 - 3 => 'weekly'
725 - ];
742 + foreach ($userPrefs as $prefKey => $prefValue) {
743 + if ($prefKey === 'message_email_frequency') {
744 + $userGlobalPrefs[$prefKey] = isset($frequencyMaps[$prefValue]) ? $frequencyMaps[$prefValue] : 'default';
745 + continue;
746 + }
726 747
727 - if (isset($maps[$pref->is_read])) {
728 - $userGlobalPrefs[$pref->notification_type] = $maps[$pref->is_read];
729 - } else {
730 - $userGlobalPrefs[$pref->notification_type] = 'default';
731 - }
732 - 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] = [];
733 753 }
734 - $userGlobalPrefs[$pref->notification_type] = $pref->is_read ? 'yes' : 'no';
735 - } else {
736 - if (empty($spaceWisePrefs[$pref->object_id])) {
737 - $spaceWisePrefs[$pref->object_id] = [];
738 - }
739 - $spaceWisePrefs[$pref->object_id][$pref->notification_type] = $pref->is_read;
754 +
755 + $spaceWisePrefs[$spaceId][$matches[1]] = $prefValue;
756 + continue;
740 757 }
758 +
759 + $userGlobalPrefs[$prefKey] = $prefValue ? 'yes' : 'no';
741 760 }
742 761
743 762 $messagingConfig = Utility::getOption('_messaging_settings', []);
744 763 $isGlobalPerUser = Arr::get($messagingConfig, 'messaging_email_frequency') == 'disabled';
745 764
765 + $pushAvailable = PushNotificationModule::isAvailable();
766 +
746 767 $userGlobalPrefsDefaults = [
747 768 'digest_mail' => Arr::get($globalPreferances, 'digest_email_status') ? 'yes' : 'no',
748 769 'mention_mail' => Arr::get($globalPreferances, 'mention_mail') ? 'yes' : 'no',
749 770 'reply_my_com_mail' => Arr::get($globalPreferances, 'reply_my_com_mail') ? 'yes' : 'no',
@@ -750,8 +771,17 @@
750 771 'com_my_post_mail' => Arr::get($globalPreferances, 'com_my_post_mail') ? 'yes' : 'no',
751 772 'message_email_frequency' => $isGlobalPerUser ? 'disabled' : 'default'
752 773 ];
753 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 +
754 784 $userGlobalPrefs = wp_parse_args($userGlobalPrefs, $userGlobalPrefsDefaults);
755 785
756 786 $profileUserId = $xProfile->user_id;
757 787 $spaceGroups = SpaceGroup::with(['spaces' => function ($query) use ($profileUserId) {
@@ -849,8 +879,16 @@
849 879 $digestDay = $maps[$digestDay];
850 880 }
851 881 }
852 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 +
853 891 $data = [
854 892 'user_globals' => (object)$userGlobalPrefs,
855 893 'spaceGroups' => $formattedSpaceGroups,
856 894 'space_prefs' => $spaceWisePrefs,
@@ -855,8 +893,10 @@
855 893 'spaceGroups' => $formattedSpaceGroups,
856 894 'space_prefs' => $spaceWisePrefs,
857 895 'digestEmailDay' => $digestDay,
858 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,
859 899 ];
860 900
861 901 return apply_filters('fluent_community/profile_notification_pref_api_response', $data, $request->all());
862 902 }
@@ -862,9 +902,9 @@
862 902 }
863 903
864 904 public function saveNotificationPreferance(Request $request, $userName)
865 905 {
866 - $xProfile = $this->verfifyAndGetProfile($userName);
906 + $xProfile = $this->verifyAndGetOwnProfile($userName);
867 907
868 908 $userPrefs = $request->get('user_globals', []);
869 909 $sapcePrefs = $request->get('space_prefs', []);
870 910
@@ -907,15 +947,75 @@
907 947 'message' => __('Email Notification preferences have been updated', 'fluent-community')
908 948 ];
909 949 }
910 950
911 - private function verfifyAndGetProfile($userName)
951 + public function reconfirmEmail(Request $request, $userName)
912 952 {
953 + if (!defined('FLUENTCRM')) {
954 + return $this->sendError([
955 + 'message' => __('FluentCRM is not available on this site', 'fluent-community')
956 + ]);
957 + }
958 +
913 959 $xProfile = XProfile::where('username', $userName)->firstOrFail();
914 960
915 - $currentUser = $this->getUser();
916 - 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)) {
917 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'));
918 1018 }
919 1019
920 1020 return $xProfile;
921 1021 }