PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 1.0.98
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v1.0.98
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 1.0.98, at app/Services/ProfileHelper.php

309 lines 14.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 return ['user_id', 'total_points', 'is_verified', 'status', 'display_name', 'username', 'avatar', 'created_at', 'last_activity', 'short_description', 'meta'];
25 }
26
27 public static function socialLinkProviders()
28 {
29 return apply_filters('fluent_community/social_link_providers', [
30 'instagram' => [
31 'title' => 'Instagram',
32 '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>',
33 'placeholder' => 'instagram @username',
34 'domain' => 'https://instagram.com/',
35 ],
36 'twitter' => [
37 'title' => 'Twitter/X',
38 'icon_svg' => '<svg viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg"><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>',
39 'placeholder' => 'twitter/X @username',
40 'domain' => 'https://x.com/',
41 ],
42 'youtube' => [
43 'title' => 'YouTube',
44 '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>',
45 'placeholder' => 'youtube @username',
46 'domain' => 'https://youtube.com/',
47 ],
48 'linkedin' => [
49 'title' => 'LinkedIn',
50 '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>',
51 'placeholder' => 'linkedin username',
52 'domain' => 'https://linkedin.com/in/',
53 ],
54 'fb' => [
55 'title' => 'Facebook',
56 'icon_svg' => '<svg width="7" height="12" viewBox="0 0 7 12" fill="none" xmlns="http://www.w3.org/2000/svg"><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>',
57 'placeholder' => 'fb username',
58 'domain' => 'https://facebook.com/',
59 ],
60 ]);
61 }
62
63 public static function getReservedUserNames()
64 {
65 return [
66 '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',
67 ];
68 }
69
70 public static function isUsernameAvailable($userName, $targetUserId = null)
71 {
72 $userName = strtolower($userName);
73
74 if (strlen($userName) < 3) {
75 return false;
76 }
77
78 $reservedUserNames = self::getReservedUserNames();
79 if (in_array($userName, $reservedUserNames)) {
80 return false;
81 }
82
83 $user = get_user_by('login', $userName);
84
85 if ($user) {
86 if ($targetUserId && $user->ID != $targetUserId) {
87 return false;
88 }
89 }
90
91 $xProfile = XProfile::where('username', $userName)
92 ->when($targetUserId, function ($query) use ($targetUserId) {
93 return $query->where('user_id', '!=', $targetUserId);
94 })
95 ->exists();
96
97 if ($xProfile) {
98 return false;
99 }
100
101 return true;
102 }
103
104 public static function generateUserName($user, $useUserName = false)
105 {
106 if (!$user instanceof \WP_User) {
107 $user = get_user_by('ID', $user);
108 }
109
110 return self::createUserNameFromStrings($user->user_login, array_filter([
111 $user->user_nicename,
112 $user->first_name,
113 $user->last_name,
114 $user->display_name
115 ]), $user->ID);
116 }
117
118 public static function createUserNameFromStrings($maybeEmail, $fallbacks = [], $userId = null)
119 {
120 $emailParts = explode('@', $maybeEmail);
121 $userName = $emailParts[0];
122
123 $userName = CustomSanitizer::sanitizeUserName($userName);
124
125 if (self::isUsernameAvailable($userName, $userId)) {
126 return $userName;
127 }
128
129 foreach ($fallbacks as $fallback) {
130 // only take alphanumeric characters and _ -
131 $fallback = preg_replace('/[^a-z0-9_-]/', '', $fallback);
132 $userName = CustomSanitizer::sanitizeUserName($fallback);
133 if (self::isUsernameAvailable($userName, $userId)) {
134 return $userName;
135 }
136 }
137
138 $userName = strtolower($emailParts[0]);
139
140 $finalUserName = $userName;
141 // loop until we find a unique username
142 $counter = 2;
143 while (!self::isUsernameAvailable($userName, $userId)) {
144 $userName = $finalUserName . $counter;
145 $counter++;
146 if ($counter % 100 === 0) {
147 $finalUserName = $finalUserName . $userId . '-';
148 }
149 }
150
151 return $userName;
152 }
153
154 public static function getUserAuthHash($userId = null)
155 {
156 if (!$userId) {
157 return '';
158 }
159
160 if (Utility::getPrivacySetting('email_auto_login') === 'no') {
161 return '';
162 }
163
164 static $cached = [];
165
166 if (isset($cached[$userId])) {
167 return $cached[$userId];
168 }
169
170 $exist = Meta::byType('user')
171 ->byMetaKey('auth_hash')
172 ->byObjectId($userId)
173 ->first();
174
175 $validTil = strtotime('+1 day');
176
177 if ($exist) {
178 $hashes = (array)$exist->value;
179
180 $validHashes = array_filter($hashes, function ($hash) use ($validTil) {
181 return $hash['valid_til'] > $validTil;
182 });
183
184 if ($validHashes) {
185 $lastHash = end($validHashes);
186 return $lastHash['hash'] . '__' . $exist->id;
187 }
188 $newHash = md5(wp_generate_uuid4() . '_' . $userId . '_' . '_' . time() . '__' . $userId);
189
190 $hashes[] = [
191 'hash' => $newHash,
192 'valid_til' => strtotime('+2 days')
193 ];
194
195 // remove expired hashes
196 $hashes = array_filter($hashes, function ($hash) use ($validTil) {
197 return $hash['valid_til'] > time();
198 });
199
200 $exist->value = array_values($hashes);
201 $exist->save();
202
203 $cached[$userId] = $newHash . '__' . $exist->id;
204
205 return $cached[$userId];
206 }
207
208 $newHash = md5(wp_generate_uuid4() . '_' . $userId . '_' . '_' . time() . '__' . $userId);
209
210 $meta = Meta::create([
211 'object_type' => 'user',
212 'object_id' => $userId,
213 'meta_key' => 'auth_hash',
214 'value' => [
215 [
216 'hash' => $newHash,
217 'valid_til' => strtotime('+2 days')
218 ]
219 ]
220 ]);
221
222 $cached[$userId] = $newHash . '__' . $meta->id;
223
224 return $cached[$userId];
225 }
226
227 public static function signUserUrlWithAuthHash($url, $userId = null)
228 {
229 $hash = self::getUserAuthHash($userId);
230 if (!$hash) {
231 return $url;
232 }
233
234 return add_query_arg([
235 'fcom_action' => 'signed_url',
236 'fcom_url_hash' => $hash
237 ], $url);
238 }
239
240 public static function getSignedNotificationPrefUrl($userId)
241 {
242 $notificationPref = Helper::baseUrl('fcom_route?route=user_notification_settings&auth=yes');
243 return self::signUserUrlWithAuthHash($notificationPref, $userId);
244 }
245
246 /*
247 * Get WP User by URL Hash
248 * @param string $hash
249 * @return \WP_User|null
250 */
251 public static function getUserByUrlHash($hash)
252 {
253 $hashParts = explode('__', $hash);
254 if (count($hashParts) !== 2) {
255 return null;
256 }
257
258 $metaId = $hashParts[1];
259 $meta = Meta::where('object_type', 'user')
260 ->where('meta_key', 'auth_hash')
261 ->where('id', $metaId)
262 ->first();
263
264 if (!$meta) {
265 return null;
266 }
267
268 $hashes = (array)$meta->value;
269 $hash = $hashParts[0];
270
271 $validHash = array_filter($hashes, function ($hashData) use ($hash) {
272 return $hashData['hash'] == $hash;
273 });
274
275 if (!$validHash) {
276 return null;
277 }
278
279 return get_user_by('ID', $meta->object_id);
280 }
281
282 public static function canViewUserSpaces($targetUserId, $currentUser = null)
283 {
284 $status = Utility::getPrivacySetting('user_space_visibility', 'everybody');
285
286 if ($status == 'everybody') {
287 return true;
288 }
289
290 if ($status == 'logged_in') {
291 return !!$currentUser;
292 }
293
294 if (is_numeric($currentUser)) {
295 $currentUser = User::find($currentUser);
296 }
297
298 if (!$currentUser) {
299 return false;
300 }
301
302 if ($targetUserId == $currentUser->ID) {
303 return true;
304 }
305
306 return Helper::isModerator($currentUser);
307 }
308 }
309