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 / Services / ProfileHelper.php

ProfileHelper.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.10.0, at app/Services/ProfileHelper.php

579 lines 32.3 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\Services;
4
5 use FluentCommunity\App\Functions\Utility;
6 use FluentCommunity\App\Models\Meta;
7 use FluentCommunity\App\Models\User;
8 use FluentCommunity\App\Models\XProfile;
9
10 class ProfileHelper
11 {
12
13 public static function getProfile($userId = null)
14 {
15 if (!$userId) {
16 $userId = get_current_user_id();
17 }
18
19 return XProfile::where('user_id', $userId)->first();
20 }
21
22 public static function getXProfilePublicFields()
23 {
24 static $fields;
25 if ($fields) {
26 return $fields;
27 }
28
29 $fields = [
30 'user_id',
31 'display_name',
32 'username',
33 'avatar',
34 'status',
35 'total_points',
36 'is_verified',
37 'meta'
38 ];
39
40 if (Utility::canViewUserProfile()) {
41 array_push($fields, 'created_at', 'short_description');
42 }
43
44 if (Utility::showLastActivity()) {
45 $fields[] = 'last_activity';
46 }
47
48 return apply_filters('fluent_community/xprofile_public_fields', $fields);
49 }
50
51 public static function getXprofileHiddenFields()
52 {
53 static $hiddenFields;
54 if ($hiddenFields) {
55 return $hiddenFields;
56 }
57
58
59 $hiddenFields = [
60 'id',
61 'created_at',
62 'updated_at'
63 ];
64
65 if (Utility::showLastActivity()) {
66 $hiddenFields[] = 'last_activity';
67 }
68
69 return $hiddenFields;
70 }
71
72 public static function socialLinkProviders($enabledOnly = false)
73 {
74 $links = apply_filters('fluent_community/social_link_providers', [
75 'instagram' => [
76 'title' => __('Instagram', 'fluent-community'),
77 'icon_svg' => '<svg viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 0C7.6302 0 7.8336 0.00599997 8.4732 0.036C9.1122 0.066 9.5472 0.1662 9.93 0.315C10.326 0.4674 10.6596 0.6738 10.9932 1.0068C11.2983 1.30674 11.5344 1.66955 11.685 2.07C11.8332 2.4522 11.934 2.8878 11.964 3.5268C11.9922 4.1664 12 4.3698 12 6C12 7.6302 11.994 7.8336 11.964 8.4732C11.934 9.1122 11.8332 9.5472 11.685 9.93C11.5348 10.3307 11.2987 10.6936 10.9932 10.9932C10.6932 11.2982 10.3304 11.5343 9.93 11.685C9.5478 11.8332 9.1122 11.934 8.4732 11.964C7.8336 11.9922 7.6302 12 6 12C4.3698 12 4.1664 11.994 3.5268 11.964C2.8878 11.934 2.4528 11.8332 2.07 11.685C1.6694 11.5347 1.30652 11.2986 1.0068 10.9932C0.701644 10.6933 0.465559 10.3305 0.315 9.93C0.1662 9.5478 0.066 9.1122 0.036 8.4732C0.00779997 7.8336 0 7.6302 0 6C0 4.3698 0.00599997 4.1664 0.036 3.5268C0.066 2.8872 0.1662 2.4528 0.315 2.07C0.465142 1.66931 0.701282 1.30639 1.0068 1.0068C1.3066 0.701539 1.66946 0.465438 2.07 0.315C2.4528 0.1662 2.8872 0.066 3.5268 0.036C4.1664 0.00779997 4.3698 0 6 0ZM6 3C5.20435 3 4.44129 3.31607 3.87868 3.87868C3.31607 4.44129 3 5.20435 3 6C3 6.79565 3.31607 7.55871 3.87868 8.12132C4.44129 8.68393 5.20435 9 6 9C6.79565 9 7.55871 8.68393 8.12132 8.12132C8.68393 7.55871 9 6.79565 9 6C9 5.20435 8.68393 4.44129 8.12132 3.87868C7.55871 3.31607 6.79565 3 6 3ZM9.9 2.85C9.9 2.65109 9.82098 2.46032 9.68033 2.31967C9.53968 2.17902 9.34891 2.1 9.15 2.1C8.95109 2.1 8.76032 2.17902 8.61967 2.31967C8.47902 2.46032 8.4 2.65109 8.4 2.85C8.4 3.04891 8.47902 3.23968 8.61967 3.38033C8.76032 3.52098 8.95109 3.6 9.15 3.6C9.34891 3.6 9.53968 3.52098 9.68033 3.38033C9.82098 3.23968 9.9 3.04891 9.9 2.85ZM6 4.2C6.47739 4.2 6.93523 4.38964 7.27279 4.72721C7.61036 5.06477 7.8 5.52261 7.8 6C7.8 6.47739 7.61036 6.93523 7.27279 7.27279C6.93523 7.61036 6.47739 7.8 6 7.8C5.52261 7.8 5.06477 7.61036 4.72721 7.27279C4.38964 6.93523 4.2 6.47739 4.2 6C4.2 5.52261 4.38964 5.06477 4.72721 4.72721C5.06477 4.38964 5.52261 4.2 6 4.2Z" fill="currentColor"/></svg>',
78 'placeholder' => 'instagram @username',
79 'domain' => 'https://instagram.com/',
80 'enabled' => 'yes'
81 ],
82 'twitter' => [
83 'title' => __('Twitter/X', 'fluent-community'),
84 'icon_svg' => '<svg viewBox="0 0 12 10" fill="none"><path d="M9.1024 0.125H10.7564L7.1429 4.255L11.3939 9.875H8.0654L5.4584 6.4665L2.47542 9.875H0.820422L4.68542 5.4575L0.607422 0.125H4.02042L6.3769 3.2405L9.1024 0.125ZM8.5219 8.885H9.4384L3.52242 1.063H2.53892L8.5219 8.885Z" fill="currentColor"/></svg>',
85 'placeholder' => 'twitter/X @username',
86 'domain' => 'https://x.com/',
87 'enabled' => 'yes'
88 ],
89 'youtube' => [
90 'title' => __('YouTube', 'fluent-community'),
91 'icon_svg' => '<svg viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.7258 1.699C12 2.7682 12 5.0002 12 5.0002C12 5.0002 12 7.2322 11.7258 8.3014C11.5734 8.8924 11.1276 9.3574 10.563 9.5146C9.5376 9.8002 6 9.8002 6 9.8002C6 9.8002 2.4642 9.8002 1.437 9.5146C0.87 9.355 0.4248 8.8906 0.2742 8.3014C1.78814e-08 7.2322 0 5.0002 0 5.0002C0 5.0002 1.78814e-08 2.7682 0.2742 1.699C0.4266 1.108 0.8724 0.642995 1.437 0.485795C2.4642 0.200195 6 0.200195 6 0.200195C6 0.200195 9.5376 0.200195 10.563 0.485795C11.13 0.645395 11.5752 1.1098 11.7258 1.699ZM4.8 7.1002L8.4 5.0002L4.8 2.9002V7.1002Z" fill="currentColor"/></svg>',
92 'placeholder' => 'youtube @username',
93 'domain' => 'https://youtube.com/',
94 'enabled' => 'yes'
95 ],
96 'linkedin' => [
97 'title' => __('LinkedIn', 'fluent-community'),
98 'icon_svg' => '<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.80061 9.8035H8.20161V7.2973C8.20161 6.6997 8.18961 5.9305 7.36761 5.9305C6.53421 5.9305 6.40701 6.5809 6.40701 7.2535V9.8035H4.80741V4.6501H6.34341V5.3521H6.36441C6.57921 4.9477 7.10121 4.5199 7.88121 4.5199C9.50121 4.5199 9.80121 5.5867 9.80121 6.9745V9.8035H9.80061ZM3.00141 3.9451C2.87934 3.94526 2.75845 3.92132 2.64565 3.87466C2.53285 3.828 2.43037 3.75954 2.34408 3.6732C2.2578 3.58686 2.1894 3.48433 2.14282 3.37151C2.09623 3.25868 2.07237 3.13776 2.07261 3.0157C2.07273 2.832 2.12732 2.65246 2.22947 2.49979C2.33163 2.34711 2.47677 2.22816 2.64653 2.15797C2.81629 2.08778 3.00305 2.06951 3.1832 2.10546C3.36334 2.14142 3.52878 2.22998 3.65859 2.35996C3.78841 2.48994 3.87676 2.65549 3.91248 2.83569C3.9482 3.01588 3.92969 3.20262 3.85928 3.37229C3.78887 3.54196 3.66973 3.68694 3.51692 3.7889C3.36412 3.89086 3.18451 3.94522 3.00081 3.9451H3.00141ZM3.80301 9.8035H2.19921V4.6501H3.80361V9.8035H3.80301ZM10.6016 0.600098H1.39701C0.955409 0.600098 0.599609 0.948098 0.599609 1.3783V10.6219C0.599609 11.0521 0.956009 11.4001 1.39641 11.4001H10.5992C11.0396 11.4001 11.3996 11.0521 11.3996 10.6219V1.3783C11.3996 0.948098 11.0396 0.600098 10.5992 0.600098H10.601H10.6016Z" fill="currentColor"/></svg>',
99 'placeholder' => 'linkedin username',
100 'domain' => 'https://linkedin.com/in/',
101 'enabled' => 'yes'
102 ],
103 'fb' => [
104 'title' => __('Facebook', 'fluent-community'),
105 'icon_svg' => '<svg width="7" height="12" viewBox="0 0 7 12" fill="none"><path d="M4.2 6.9H5.7L6.3 4.5H4.2V3.3C4.2 2.682 4.2 2.1 5.4 2.1H6.3V0.0840001C6.1044 0.0582001 5.3658 0 4.5858 0C2.9568 0 1.8 0.9942 1.8 2.82V4.5H0V6.9H1.8V12H4.2V6.9Z" fill="currentColor"/></svg>',
106 'placeholder' => 'fb_username',
107 'domain' => 'https://facebook.com/',
108 'enabled' => 'yes'
109 ],
110 'blue_sky' => [
111 'title' => __('Bluesky', 'fluent-community'),
112 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><path d="M12 11.4963C11.8936 11.2963 7.45492 3 3.50417 3C1.33647 3 2.00456 8 2.50443 10.5C2.70653 11.5108 3.50417 14.5 8.003 14C8.003 14 4.00404 14.5 4.00404 17C4.00404 18.5 6.50339 21 8.50287 21C10.4606 21 11.9391 16.6859 12 16.5058C12.0609 16.6859 13.5394 21 15.4971 21C17.4966 21 19.996 18.5 19.996 17C19.996 14.5 15.997 14 15.997 14C20.4958 14.5 21.2935 11.5108 21.4956 10.5C21.9954 8 22.6635 3 20.4958 3C16.5451 3 12.1064 11.2963 12 11.4963Z" fill="currentColor" /></svg>',
113 'placeholder' => 'bluesky_username',
114 'domain' => 'https://bsky.app/profile/',
115 'enabled' => 'no'
116 ],
117 'tiktok' => [
118 'title' => __('TikTok', 'fluent-community'),
119 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 1.75C2.98122 1.75 1.75 2.98122 1.75 4.5V19.5C1.75 21.0188 2.98122 22.25 4.5 22.25H19.5C21.0188 22.25 22.25 21.0188 22.25 19.5V4.5C22.25 2.98122 21.0188 1.75 19.5 1.75H4.5ZM14.5 6C14.5 5.44772 14.0523 5 13.5 5C12.9477 5 12.5 5.44772 12.5 6V14.5C12.5 15.8807 11.3807 17 10 17C8.61929 17 7.5 15.8807 7.5 14.5C7.5 13.1193 8.61929 12 10 12C10.5523 12 11 11.5523 11 11C11 10.4477 10.5523 10 10 10C7.51472 10 5.5 12.0147 5.5 14.5C5.5 16.9853 7.51472 19 10 19C12.4853 19 14.5 16.9853 14.5 14.5V9.56299C15.3659 10.1552 16.4276 10.5 17.5 10.5C18.0523 10.5 18.5 10.0523 18.5 9.5C18.5 8.94772 18.0523 8.5 17.5 8.5C16.7397 8.5 15.9649 8.21074 15.3902 7.73178C14.8206 7.25714 14.5 6.64642 14.5 6Z" fill="currentColor" /></svg>',
120 'placeholder' => '@tiktok_username',
121 'domain' => 'https://tiktok.com/',
122 'enabled' => 'no'
123 ],
124 'pinterest' => [
125 'title' => __('Pinterest', 'fluent-community'),
126 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C6.06294 1.25 1.25 6.06294 1.25 12C1.25 16.0389 3.47738 19.5576 6.77079 21.3947L11.0721 10.629C11.277 10.1162 11.8589 9.86654 12.3717 10.0714C12.8846 10.2764 13.1343 10.8582 12.9293 11.3711L11.1187 15.9028C11.4018 15.9664 11.6966 16 12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 12.7302 8.19488 13.4118 8.53476 13.9992C8.81136 14.4772 8.64807 15.0889 8.17005 15.3655C7.69202 15.6421 7.08027 15.4789 6.80367 15.0008C6.29247 14.1174 6 13.0914 6 12C6 8.68629 8.68629 6 12 6C15.3137 6 18 8.68629 18 12C18 15.3137 15.3137 18 12 18C11.4361 18 10.8893 17.9219 10.3704 17.7758L8.60199 22.2019C9.67002 22.5575 10.8125 22.75 12 22.75C17.9371 22.75 22.75 17.9371 22.75 12C22.75 6.06294 17.9371 1.25 12 1.25Z" fill="currentColor" /></svg>',
127 'placeholder' => 'pinterest_username',
128 'domain' => 'https://pinterest.com/',
129 'enabled' => 'no'
130 ],
131 'telegram' => [
132 'title' => __('Telegram', 'fluent-community'),
133 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M22.7317 3.16269C22.792 2.89122 22.6972 2.60865 22.4853 2.42854C22.2734 2.24844 21.9792 2.20037 21.721 2.30366L1.72146 10.3037C1.45174 10.4115 1.26858 10.6655 1.25133 10.9555C1.23408 11.2455 1.38584 11.5193 1.64087 11.6584L6.82025 14.4836L7.76021 20.1233C7.8077 20.4083 8.01463 20.6406 8.2922 20.7206C8.56977 20.8007 8.86864 20.7142 9.06056 20.4983L11.6108 17.6293L9.49049 15.2837C9.21881 14.9831 9.23494 14.5211 9.52692 14.2403L13.7768 10.1519L14.6708 9.23818C14.9604 8.94211 15.4353 8.93693 15.7314 9.22661C16.0274 9.5163 16.0326 9.99114 15.7429 10.2872L14.841 11.209C14.8357 11.2144 14.8303 11.2197 14.8249 11.225L11.0908 14.8172L14.2359 18.2966L17.4697 21.5303C17.6632 21.7239 17.9469 21.7966 18.2097 21.7201C18.4725 21.6436 18.6728 21.4299 18.7321 21.1627L22.7317 3.16269Z" fill="currentColor" /></svg>',
134 'placeholder' => 'telegram_username',
135 'domain' => 'https://telegram.me/',
136 'enabled' => 'no'
137 ],
138 'snapchat' => [
139 'title' => __('Snapchat', 'fluent-community'),
140 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><path d="M2 16.5C5.82356 14.0006 5.82356 11.5404 2.95589 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M22 16.5C18.1764 14.0006 18.1764 11.5404 21.0441 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M6.57556 7.42444C6.57556 4.42861 9.00416 2 12 2C14.9958 2 17.4244 4.42861 17.4244 7.42444C17.4244 12.1722 17.6611 14.5456 22 16.4444C19.7778 17 19.0556 17.2778 18.5 19.5C14.6111 19.5 14.2222 22 12 22C9.77778 22 9.38889 19.5 5.5 19.5C4.94444 17.2778 4.22222 17.0556 2 16.5C6.33889 14.6011 6.57556 12.1722 6.57556 7.42444Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>',
141 'placeholder' => 'snapchat_username',
142 'domain' => 'https://snapchat.com/add/',
143 'enabled' => 'no'
144 ],
145 'reddit' => [
146 'title' => __('Reddit', 'fluent-community'),
147 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><ellipse cx="12" cy="15.5" rx="9" ry="6.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M15.5 16.7803C14.5149 17.548 13.3062 18.0002 12 18.0002C10.6938 18.0002 9.48512 17.548 8.5 16.7803" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="19" cy="4" r="2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M18 10.0694C18.3687 9.43053 19.0634 9 19.8595 9C21.0417 9 22 9.94921 22 11.1201C22 11.937 21.5336 12.6459 20.8502 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M6 10.0694C5.63125 9.43053 4.93663 9 4.14048 9C2.95833 9 2 9.94921 2 11.1201C2 11.937 2.4664 12.6459 3.14981 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M12 9V6C12 4.89543 12.8954 4 14 4H17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M9.00801 13L8.99902 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M15.008 13L14.999 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>',
148 'placeholder' => 'reddit_username',
149 'domain' => 'https://www.reddit.com/user/',
150 'enabled' => 'no'
151 ],
152 'twitch' => [
153 'title' => __('Twitch', 'fluent-community'),
154 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><path d="M16 7V11M12 7V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 3H8C6.11438 3 5.17157 3 4.58579 3.58358C4 4.16716 4 5.10641 4 6.98492V13.56C4 13.9302 4 14.1153 4.02462 14.2702C4.16017 15.1228 4.83135 15.7914 5.68713 15.9265C5.8426 15.951 6.0284 15.951 6.4 15.951C6.4929 15.951 6.53935 15.951 6.57822 15.9571C6.79216 15.9909 6.95996 16.158 6.99384 16.3712C7 16.4099 7 16.4562 7 16.5487V18.0921C7 19.2742 7 19.8653 7.3345 19.9822C7.66899 20.0991 8.03962 19.6375 8.78087 18.7144L10.6998 16.3249C10.8473 16.1412 10.921 16.0493 11.0237 16.0002C11.1264 15.951 11.2445 15.951 11.4806 15.951H15.3431C16.1606 15.951 16.5694 15.951 16.9369 15.7993C17.3045 15.6477 17.5935 15.3597 18.1716 14.7838L18.8284 14.1295C19.4065 13.5536 19.6955 13.2656 19.8478 12.8995C20 12.5333 20 12.1261 20 11.3117V6.98492C20 5.10641 20 4.16716 19.4142 3.58358C18.8284 3 17.8856 3 16 3Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>',
155 'placeholder' => 'twitch_username',
156 'domain' => 'https://www.twitch.tv/',
157 'enabled' => 'no'
158 ],
159 'vk' => [
160 'title' => __('VK', 'fluent-community'),
161 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><path d="M2.00053 5.5H5.50053C5.50053 13.5 10.0005 14.5 10.0005 14.5L10.0015 5.5H13.5015L13.4995 10.5C17.9995 8.5 18.4995 5.5 18.4995 5.5H21.9995C21.9995 5.5 20.9995 10 17.0926 12.1534C19.1115 13.3511 21.2684 15.3315 21.9995 18.5H18.4995C18.4995 18.5 17.4995 15.5 13.4995 14L13.5015 18.5C1.88755 18.5 2.00232 7.5 2.00053 5.5Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>',
162 'placeholder' => __('vk username', 'fluent-community'),
163 'domain' => 'https://vk.com/',
164 'enabled' => 'yes'
165 ],
166 'github' => [
167 'title' => __('Github', 'fluent-community'),
168 'icon_svg' => '<svg height="32" viewBox="0 0 24 24" version="1.1" width="32" color="currentColor"><path d="M12.5.75C6.146.75 1 5.896 1 12.25c0 5.089 3.292 9.387 7.863 10.91.575.101.79-.244.79-.546 0-.273-.014-1.178-.014-2.142-2.889.532-3.636-.704-3.866-1.35-.13-.331-.69-1.352-1.18-1.625-.402-.216-.977-.748-.014-.762.906-.014 1.553.834 1.769 1.179 1.035 1.74 2.688 1.25 3.349.948.1-.747.402-1.25.733-1.538-2.559-.287-5.232-1.279-5.232-5.678 0-1.25.445-2.285 1.178-3.09-.115-.288-.517-1.467.115-3.048 0 0 .963-.302 3.163 1.179.92-.259 1.897-.388 2.875-.388.977 0 1.955.13 2.875.388 2.2-1.495 3.162-1.179 3.162-1.179.633 1.581.23 2.76.115 3.048.733.805 1.179 1.825 1.179 3.09 0 4.413-2.688 5.39-5.247 5.678.417.36.776 1.05.776 2.128 0 1.538-.014 2.774-.014 3.162 0 .302.216.662.79.547C20.709 21.637 24 17.324 24 12.25 24 5.896 18.854.75 12.5.75Z"></path></svg>',
169 'placeholder' => 'github username',
170 'domain' => 'https://github.com/',
171 'enabled' => 'yes'
172 ],
173 'mastodon' => [
174 'title' => __('Mastodon', 'fluent-community'),
175 'icon_svg' => '<svg viewBox="0 0 24 24" width="20" height="20" color="currentColor" fill="none"><path d="M17 13.5V8C17 6.61929 15.8807 5.5 14.5 5.5C13.1193 5.5 12 6.61929 12 8M12 8V11.5M12 8C12 6.61929 10.8807 5.5 9.5 5.5C8.11929 5.5 7 6.61929 7 8V13.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M13 16.9954C15.0099 16.9954 16.89 16.6876 18.4949 16.1525C20.1275 15.6081 21 13.9512 21 12.2302V7.52349C21 5.34784 19.8297 3.2779 17.7281 2.715C16.0259 2.25905 14.0744 2 12 2C9.9256 2 7.97414 2.25905 6.27189 2.715C4.17033 3.2779 3 5.34785 3 7.52349V14.4961C3 22.4937 11 21.9938 11 21.9938C13.5 21.9938 15 21 15 21V20C15 20 13.5 20.4943 11 20.4943C5.68009 20.4943 7.06011 15.9957 7.06011 15.9957C8.75781 16.627 10.8012 16.9954 13 16.9954Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>',
176 'placeholder' => '@mastodon_username',
177 'domain' => 'https://mastodon.social/',
178 'enabled' => 'yes'
179 ]
180 ]);
181
182 $enabledKeys = self::getEnabledLinkProviderKeys();
183
184 foreach ($links as $key => $provider) {
185 if (in_array($key, $enabledKeys)) {
186 $links[$key]['enabled'] = 'yes';
187 } else {
188 $links[$key]['enabled'] = 'no';
189 }
190
191 if (!empty($provider['icon_svg'])) {
192 $links[$key]['icon_svg'] = CustomSanitizer::sanitizeSvg($provider['icon_svg']);
193 }
194 }
195
196 if ($enabledOnly) {
197 $links = array_filter($links, function ($link) {
198 return $link['enabled'] === 'yes';
199 });
200 }
201
202 return $links;
203 }
204
205 public static function getEnabledLinkProviderKeys()
206 {
207 $keys = Utility::getOption('enabled_profile_link_keys', NULL);
208 if ($keys === NULL) {
209 return ['instagram', 'twitter', 'youtube', 'linkedin', 'fb'];
210 }
211
212 return $keys;
213 }
214
215 public static function getReservedUserNames()
216 {
217 return apply_filters('fluent_community/reserved_usernames', [
218 'admin', 'administrator', 'me', 'moderator', 'mod', 'superuser', 'root', 'system', 'official', 'staff', 'support', 'helpdesk', 'user', 'guest', 'anonymous', 'everyone', 'anybody', 'someone', 'webmaster', 'postmaster', 'hostmaster', 'abuse', 'security', 'ssl', 'firewall', 'no-reply', 'noreply', 'mail', 'email', 'mailer', 'smtp', 'pop', 'imap', 'ftp', 'sftp', 'ssh', 'ceo', 'cfo', 'cto', 'founder', 'cofounder', 'owner', 'president', 'vicepresident', 'director', 'manager', 'supervisor', 'executive', 'info', 'contact', 'sales', 'marketing', 'support', 'billing', 'accounting', 'finance', 'hr', 'humanresources', 'legal', 'compliance', 'it', 'itsupport', 'customerservice', 'customersupport', 'dev', 'developer', 'api', 'sdk', 'app', 'bot', 'chatbot', 'sysadmin', 'devops', 'infosec', 'security', 'test', 'testing', 'beta', 'alpha', 'staging', 'production', 'development', 'home', 'about', 'contact', 'faq', 'help', 'news', 'blog', 'forum', 'community', 'events', 'calendar', 'shop', 'store', 'cart', 'checkout', 'social', 'follow', 'like', 'share', 'tweet', 'post', 'status', 'privacy', 'terms', 'copyright', 'trademark', 'legal', 'policy', 'all', 'none', 'null', 'undefined', 'true', 'false', 'default', 'example', 'sample', 'demo', 'temporary', 'delete', 'remove', 'profanity', 'explicit', 'offensive', 'yourappname', 'yourbrandname', 'yourdomain',
219 ]);
220 }
221
222 public static function isUsernameAvailable($userName, $targetUserId = null)
223 {
224 $userName = strtolower($userName);
225
226 if (strlen($userName) < 3) {
227 return false;
228 }
229
230 $reservedUserNames = self::getReservedUserNames();
231 if (in_array($userName, $reservedUserNames)) {
232 return false;
233 }
234
235 $user = get_user_by('login', $userName);
236
237 if ($user) {
238 if ($targetUserId && $user->ID != $targetUserId) {
239 return false;
240 }
241 }
242
243 $xProfile = XProfile::where('username', $userName)
244 ->when($targetUserId, function ($query) use ($targetUserId) {
245 return $query->where('user_id', '!=', $targetUserId);
246 })
247 ->exists();
248
249 if ($xProfile) {
250 return false;
251 }
252
253 return true;
254 }
255
256 public static function generateUserName($user, $useUserName = false)
257 {
258 if (!$user instanceof \WP_User) {
259 $user = get_user_by('ID', $user);
260 }
261
262 return self::createUserNameFromStrings($user->user_login, array_filter([
263 $user->user_nicename,
264 $user->first_name,
265 $user->last_name,
266 $user->display_name
267 ]), $user->ID);
268 }
269
270 public static function createUserNameFromStrings($maybeEmail, $fallbacks = [], $userId = null)
271 {
272 $emailParts = explode('@', $maybeEmail);
273 $userName = $emailParts[0];
274
275 $userName = CustomSanitizer::sanitizeUserName($userName);
276
277 if (self::isUsernameAvailable($userName, $userId)) {
278 return $userName;
279 }
280
281 foreach ($fallbacks as $fallback) {
282 // only take alphanumeric characters and _ -
283 $fallback = preg_replace('/[^a-zA-Z0-9_-]/', '', $fallback);
284 $userName = CustomSanitizer::sanitizeUserName($fallback);
285 if (self::isUsernameAvailable($userName, $userId)) {
286 return $userName;
287 }
288 }
289
290 $userName = strtolower($emailParts[0]);
291
292 $finalUserName = $userName;
293 // loop until we find a unique username
294 $counter = 2;
295 while (!self::isUsernameAvailable($userName, $userId)) {
296 $userName = $finalUserName . $counter;
297 $counter++;
298 if ($counter % 100 === 0) {
299 $finalUserName = $finalUserName . $userId . '-';
300 }
301 }
302
303 return $userName;
304 }
305
306 public static function createWpUser(array $userData)
307 {
308 $email = sanitize_email((string) ($userData['email'] ?? ''));
309 $fullName = trim((string) ($userData['full_name'] ?? ''));
310 $password = trim((string) ($userData['password'] ?? ''));
311 $username = sanitize_user((string) ($userData['username'] ?? ''), true);
312
313 if (!is_email($email)) {
314 return new \WP_Error('invalid_email', __('Invalid email address', 'fluent-community'));
315 }
316
317 if (!$password) {
318 $password = wp_generate_password(12);
319 }
320
321 $nameParts = explode(' ', $fullName);
322 $firstName = array_shift($nameParts);
323 $lastName = implode(' ', $nameParts);
324
325 if ($username && self::isUsernameAvailable($username)) {
326 $userLogin = $username;
327 } else {
328 $userLogin = self::createUserNameFromStrings($email, array_filter([$firstName, $lastName]));
329 }
330
331 $role = apply_filters('fluent_community/created_user_role', 'subscriber', $userData);
332
333 return wp_insert_user([
334 'role' => $role,
335 'user_email' => $email,
336 'user_login' => $userLogin,
337 'user_pass' => $password,
338 'first_name' => sanitize_text_field($firstName),
339 'last_name' => sanitize_text_field($lastName),
340 ]);
341 }
342
343 public static function getUserAuthHash($userId = null)
344 {
345 if (!$userId) {
346 return '';
347 }
348
349 if (Utility::getPrivacySetting('email_auto_login') === 'no') {
350 return '';
351 }
352
353 static $cached = [];
354
355 if (isset($cached[$userId])) {
356 return $cached[$userId];
357 }
358
359 $exist = Meta::byType('user')
360 ->byMetaKey('auth_hash')
361 ->byObjectId($userId)
362 ->first();
363
364 $validTil = strtotime('+1 day');
365
366 if ($exist) {
367 $hashes = (array)$exist->value;
368
369 $validHashes = array_filter($hashes, function ($hash) use ($validTil) {
370 return $hash['valid_til'] > $validTil;
371 });
372
373 if ($validHashes) {
374 $lastHash = end($validHashes);
375 return $lastHash['hash'] . '__' . $exist->id;
376 }
377 $newHash = bin2hex(random_bytes(32));
378
379 $hashes[] = [
380 'hash' => $newHash,
381 'valid_til' => strtotime('+2 days')
382 ];
383
384 // remove expired hashes
385 $hashes = array_filter($hashes, function ($hash) use ($validTil) {
386 return $hash['valid_til'] > time();
387 });
388
389 $exist->value = array_values($hashes);
390 $exist->save();
391
392 $cached[$userId] = $newHash . '__' . $exist->id;
393
394 return $cached[$userId];
395 }
396
397 $newHash = bin2hex(random_bytes(32));
398
399 $meta = Meta::create([
400 'object_type' => 'user',
401 'object_id' => $userId,
402 'meta_key' => 'auth_hash', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
403 'value' => [
404 [
405 'hash' => $newHash,
406 'valid_til' => strtotime('+2 days')
407 ]
408 ]
409 ]);
410
411 $cached[$userId] = $newHash . '__' . $meta->id;
412
413 return $cached[$userId];
414 }
415
416 public static function signUserUrlWithAuthHash($url, $userId = null)
417 {
418 $hash = self::getUserAuthHash($userId);
419 if (!$hash) {
420 return $url;
421 }
422
423 return add_query_arg([
424 'fcom_action' => 'signed_url',
425 'fcom_url_hash' => $hash
426 ], $url);
427 }
428
429 public static function getSignedNotificationPrefUrl($userId)
430 {
431 $notificationPref = Helper::baseUrl('fcom_route?route=user_notification_settings&auth=yes');
432 return self::signUserUrlWithAuthHash($notificationPref, $userId);
433 }
434
435 /*
436 * Get WP User by URL Hash
437 * @param string $hash
438 * @return \WP_User|null
439 */
440 public static function getUserByUrlHash($hash)
441 {
442 $hashParts = explode('__', $hash);
443 if (count($hashParts) !== 2) {
444 return null;
445 }
446
447 $metaId = $hashParts[1];
448 $meta = Meta::where('object_type', 'user')
449 ->where('meta_key', 'auth_hash')
450 ->where('id', $metaId)
451 ->first();
452
453 if (!$meta) {
454 return null;
455 }
456
457 $hashes = (array)$meta->value;
458 $hash = $hashParts[0];
459
460 $validHash = array_filter($hashes, function ($hashData) use ($hash) {
461 if (!is_array($hashData) || empty($hashData['hash']) || empty($hashData['valid_til'])) {
462 return false;
463 }
464
465 if ((int)$hashData['valid_til'] <= time()) {
466 return false;
467 }
468
469 return hash_equals((string)$hashData['hash'], (string)$hash);
470 });
471
472 if (!$validHash) {
473 return null;
474 }
475
476 // Consume the hash (single-use) to prevent replay attacks
477 $remainingHashes = array_filter($hashes, function ($hashData) use ($hash) {
478 return !is_array($hashData) || empty($hashData['hash']) || !hash_equals((string)$hashData['hash'], (string)$hash);
479 });
480 $meta->value = array_values($remainingHashes);
481
482 if (!$meta->save()) {
483 return null;
484 }
485
486 return get_user_by('ID', $meta->object_id);
487 }
488
489 public static function canViewUserSpaces($targetUserId, $currentUser = null)
490 {
491 $status = Utility::getPrivacySetting('user_space_visibility', 'everybody');
492
493 if ($status == 'everybody') {
494 return true;
495 }
496
497 if ($status == 'logged_in') {
498 return !!$currentUser;
499 }
500
501 if (is_numeric($currentUser)) {
502 $currentUser = User::find($currentUser);
503 }
504
505 if (!$currentUser) {
506 return false;
507 }
508
509 if ($targetUserId == $currentUser->ID) {
510 return true;
511 }
512
513 return Helper::isModerator($currentUser);
514 }
515
516
517 /**
518 * Sends a confirmation request email when a change of user email address is attempted.
519 *
520 * @param \WP_User $current_user The current user object.
521 * @param $new_email string The new email address.
522 * @return \WP_Error|boolean $error WP_Error object.
523 */
524 public static function sendConfirmationOnProfileEmailChange(\WP_User $current_user, $newEmail)
525 {
526 if ($current_user->user_email == $newEmail) {
527 return false;
528 }
529
530 if (!is_email($newEmail)) {
531 return new \WP_Error('user_email', __('Error: The email address is not correct.', 'fluent-community'));
532 }
533
534 if (email_exists($newEmail)) {
535 delete_user_meta($current_user->ID, '_new_email');
536 return new \WP_Error('user_email', __('Error: The email address is already used.', 'fluent-community'));
537 }
538
539 $hash = md5($newEmail . time() . wp_rand());
540 $new_user_email = array(
541 'hash' => $hash,
542 'newemail' => $newEmail
543 );
544 update_user_meta($current_user->ID, '_new_email', $new_user_email);
545
546 $sitename = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
547
548 /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
549 $email_text = __(
550 'Howdy ###USERNAME###,
551
552 You recently requested to have the email address on your account changed.
553
554 If this is correct, please click on the following link to change it:
555 ###ADMIN_URL###
556
557 You can safely ignore and delete this email if you do not want to
558 take this action.
559
560 This email has been sent to ###EMAIL###
561
562 Regards,
563 All at ###SITENAME###
564 ###SITEURL###', 'fluent-community'
565 );
566
567 $content = apply_filters('new_user_email_content', $email_text, $new_user_email); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
568
569 $content = str_replace('###USERNAME###', $current_user->user_login, $content);
570 $content = str_replace('###ADMIN_URL###', esc_url(self_admin_url('profile.php?newuseremail=' . $hash)), $content);
571 $content = str_replace('###EMAIL###', $newEmail, $content);
572 $content = str_replace('###SITENAME###', $sitename, $content);
573 $content = str_replace('###SITEURL###', home_url(), $content);
574
575 /* translators: New email address notification email subject. %s: Site title. */
576 return wp_mail($newEmail, sprintf(__('[%s] Email Change Request', 'fluent-community'), $sitename), $content);
577 }
578 }
579