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 / Functions / Utility.php

Utility.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.8.1, at app/Functions/Utility.php

1,286 lines 50.8 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\Functions;
4
5 use FluentCommunity\App\App;
6 use FluentCommunity\App\Models\Meta;
7 use FluentCommunity\App\Models\Space;
8 use FluentCommunity\App\Models\SpaceGroup;
9 use FluentCommunity\App\Models\Term;
10 use FluentCommunity\App\Services\FeedsHelper;
11 use FluentCommunity\App\Services\Helper;
12 use FluentCommunity\Framework\Support\Arr;
13 use FluentCommunity\Modules\Course\Model\Course;
14
15 class Utility
16 {
17 public static function isDev()
18 {
19 static $isDev = null;
20 if ($isDev !== null) {
21 return $isDev;
22 }
23
24 $config = App::getInstance()->config;
25 $isDev = $config->get('app.env') === 'dev';
26 return $isDev;
27 }
28
29 public static function getApp($instance = null)
30 {
31 return \FluentCommunity\App\App::getInstance($instance);
32 }
33
34 public static function extender()
35 {
36 return new FluentExtendApi();
37 }
38
39 /**
40 * Get Global Fluent Community Option
41 * @param string $key The option name
42 * @param mixed $default the default value of the option if option is not available
43 * @return mixed
44 */
45 public static function getOption($key, $default = null)
46 {
47 return self::getFromCache('option_' . $key, function () use ($key, $default) {
48 $exist = \FluentCommunity\App\Models\Meta::where('object_type', 'option')
49 ->where('meta_key', $key)
50 ->first();
51
52 if ($exist) {
53 return $exist->value;
54 }
55
56 return $default;
57 });
58 }
59
60 /**
61 * Update Global Fluent Community Option
62 * @param string $key The option name
63 * @param mixed $value the value of the option
64 * @return \FluentCommunity\App\Models\Meta
65 */
66 public static function updateOption($key, $value)
67 {
68 $exist = \FluentCommunity\App\Models\Meta::where('object_type', 'option')
69 ->where('meta_key', $key)
70 ->first();
71 if ($exist) {
72 $exist->value = $value;
73 $exist->save();
74
75 } else {
76 $exist = \FluentCommunity\App\Models\Meta::create([
77 'meta_key' => $key, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
78 'object_type' => 'option',
79 'value' => $value
80 ]);
81 }
82
83 self::setCache('option_' . $key, $value);
84
85 return $exist;
86 }
87
88 public static function deleteOption($key)
89 {
90 \FluentCommunity\App\Models\Meta::where('object_type', 'option')
91 ->where('meta_key', $key)
92 ->delete();
93
94 self::forgetCache('option_' . $key);
95 }
96
97 public static function getFeaturesConfig()
98 {
99 static $features = null;
100
101 if ($features) {
102 return $features;
103 }
104
105 $features = self::getOption('fluent_community_features', []);
106
107 $defaults = [
108 'leader_board_module' => 'yes',
109 'course_module' => 'yes',
110 'giphy_module' => 'no',
111 'giphy_api_key' => '',
112 'emoji_module' => 'yes',
113 'cloud_storage' => 'no',
114 'invitation' => 'yes',
115 'user_badge' => 'yes',
116 'has_crm_sync' => 'no',
117 'content_moderation' => 'no',
118 'followers_module' => 'no',
119 'custom_profile_fields' => 'no',
120 'pwa_module' => 'no',
121 ];
122
123 if (defined('FLUENT_COMMUNITY_CLOUD_STORAGE') && FLUENT_COMMUNITY_CLOUD_STORAGE) {
124 $features['cloud_storage'] = 'yes';
125 }
126
127 $features = wp_parse_args($features, $defaults);
128
129 $hasPro = defined('FLUENT_COMMUNITY_PRO') && FLUENT_COMMUNITY_PRO;
130
131 if (!$hasPro) {
132 $features['leader_board_module'] = 'no';
133 $features['giphy_module'] = 'no';
134 $features['emoji_module'] = 'no';
135 $features['cloud_storage'] = 'no';
136 $features['user_badge'] = 'no';
137 $features['followers_module'] = 'no';
138 $features['custom_profile_fields'] = 'no';
139 $features['pwa_module'] = 'no';
140 }
141
142 return $features;
143 }
144
145 /**
146 * @param $key
147 * @param $callback
148 * @param $expire
149 * @return false|mixed
150 * @internal Internal Function
151 */
152 public static function getFromCache($key, $callback = false, $expire = 3600)
153 {
154 $key = 'focm_' . $key;
155
156 $value = wp_cache_get($key, 'fluent_community');
157
158 if ($value !== false) {
159 return $value;
160 }
161
162 if ($callback) {
163 $value = $callback();
164 if ($value) {
165 wp_cache_set($key, $value, 'fluent_community', $expire);
166 }
167 }
168
169 return $value;
170 }
171
172 /**
173 * @param $key
174 * @param $value
175 * @param $expire
176 * @return bool
177 * @internal Internal Function
178 */
179 public static function setCache($key, $value, $expire = 600)
180 {
181 $key = 'focm_' . $key;
182
183 return wp_cache_set($key, $value, 'fluent_community', $expire);
184 }
185
186 /**
187 * @param $key
188 * @return bool
189 * @internal Internal Function
190 */
191 public static function forgetCache($key)
192 {
193 $key = 'focm_' . $key;
194 return wp_cache_delete($key, 'fluent_community');
195 }
196
197 public static function getCustomizationSettings()
198 {
199 static $settings;
200
201 if ($settings) {
202 return $settings;
203 }
204
205 $defaults = [
206 'dark_mode' => 'yes',
207 'default_theme_mode' => 'light',
208 'fixed_page_header' => 'yes',
209 'show_powered_by' => 'yes',
210 'feed_link_on_sidebar' => 'yes',
211 'show_post_modal' => 'yes',
212 'fixed_sidebar' => 'no',
213 'icon_on_header_menu' => 'no',
214 'affiliate_id' => '',
215 'rich_post_layout' => 'classic',
216 'member_list_layout' => 'classic', // grid, classic
217 'default_feed_layout' => 'timeline', // list, timeline
218 'disable_feed_layout' => 'no',
219 'post_title_pref' => 'optional',
220 'max_media_per_post' => 4,
221 'disable_feed_sort_by' => 'no',
222 'default_feed_sort_by' => '',
223 'collapse_sidebar_groups' => 'no',
224 'hide_header_on_scroll' => 'no',
225 'enable_sidebar_toggle' => 'no'
226 ];
227 $settings = self::getOption('customization_settings', $defaults);
228
229 $settings = wp_parse_args($settings, $defaults);
230 $settings = apply_filters('fluent_community/customization_settings', $settings);
231 if (!defined('FLUENT_COMMUNITY_PRO')) {
232 $settings['show_powered_by'] = 'yes';
233 $settings['affiliate_id'] = '';
234 $settings['rich_post_layout'] = 'classic';
235 $settings['member_list_layout'] = 'classic';
236 $settings['enable_sidebar_toggle'] = 'no';
237 }
238 return $settings;
239 }
240
241 public static function getCustomizationSetting($key)
242 {
243 $settings = self::getCustomizationSettings();
244 return Arr::get($settings, $key);
245 }
246
247 public static function updateCustomizationSettings($settings)
248 {
249 $preSettings = self::getCustomizationSettings();
250 $settings = Arr::only($settings, array_keys($preSettings));
251
252 self::updateOption('customization_settings', $settings);
253
254 return $settings;
255 }
256
257 public static function getPrivacySettings()
258 {
259 static $settings;
260
261 if ($settings) {
262 return $settings;
263 }
264
265 $defaults = [
266 'can_customize_username' => 'no',
267 'can_change_email' => 'no',
268 'can_change_password' => 'yes',
269 'show_last_activity' => 'yes',
270 'can_deactive_account' => 'no',
271 'email_auto_login' => 'yes',
272 'enable_gravatar' => 'yes',
273 'enable_user_sync' => 'yes',
274 'skip_crm_undeliverable_emails' => 'yes',
275 'members_page_status' => 'everybody', // everybody, logged_in, admin_only
276 'profile_page_visibility' => 'everybody', // everybody, logged_in, admin_only
277 'user_space_visibility' => 'everybody', // everybody, logged_in, admin_only
278 'leaderboard_members_visibility' => 'everybody', // everybody, logged_in, admin_only
279 ];
280
281 $settings = self::getOption('privacy_settings', $defaults);
282
283 $settings = wp_parse_args($settings, $defaults);
284
285 if (!defined('FLUENT_COMMUNITY_PRO')) {
286 $settings['can_customize_username'] = 'no';
287 $settings['can_change_email'] = 'no';
288 $settings['email_auto_login'] = 'no';
289 }
290
291 return $settings;
292 }
293
294 public static function canViewMembersPage()
295 {
296 $pageStatus = self::getPrivacySetting('members_page_status');
297
298 if ($pageStatus == 'everybody') {
299 return apply_filters('fluent_community/can_view_members_page', true, $pageStatus);
300 }
301
302 if ($pageStatus == 'logged_in') {
303 return apply_filters('fluent_community/can_view_members_page', is_user_logged_in(), $pageStatus);
304 }
305
306 return apply_filters('fluent_community/can_view_members_page', Helper::isModerator(), $pageStatus);
307 }
308
309 public static function canViewLeaderboardMembers()
310 {
311 $pageStatus = self::getPrivacySetting('leaderboard_members_visibility');
312
313 if ($pageStatus == 'everybody') {
314 return apply_filters('fluent_community/can_view_leaderboard_members', true, $pageStatus);
315 }
316
317 if ($pageStatus == 'logged_in') {
318 return apply_filters('fluent_community/can_view_leaderboard_members', is_user_logged_in(), $pageStatus);
319 }
320
321 return apply_filters('fluent_community/can_view_leaderboard_members', Helper::isModerator(), $pageStatus);
322 }
323
324 public static function canViewUserProfile($targetUserId = null)
325 {
326 $pageStatus = self::getPrivacySetting('profile_page_visibility');
327
328 if ($pageStatus == 'everybody') {
329 return apply_filters('fluent_community/can_view_user_profile', true, $pageStatus, $targetUserId);
330 }
331
332 if ($pageStatus == 'logged_in') {
333 return apply_filters('fluent_community/can_view_user_profile', is_user_logged_in(), $pageStatus, $targetUserId);
334 }
335
336 $isOwn = $targetUserId && (get_current_user_id() === $targetUserId);
337
338 return apply_filters('fluent_community/can_view_user_profile', ($isOwn || Helper::isModerator()), $pageStatus, $targetUserId);
339 }
340
341 public static function getPrivacySetting($key)
342 {
343 $settings = self::getPrivacySettings();
344 return Arr::get($settings, $key);
345 }
346
347 public static function updatePrivacySettings($settings)
348 {
349 $preSettings = self::getPrivacySettings();
350 $settings = Arr::only($settings, array_keys($preSettings));
351
352 $settings = array_map(function ($value) {
353 return is_scalar($value) ? sanitize_text_field($value) : '';
354 }, $settings);
355
356 self::updateOption('privacy_settings', $settings);
357 self::forgetCache('privacy_settings');
358
359 return $settings;
360 }
361
362 public static function showLastActivity()
363 {
364 return self::getPrivacySetting('show_last_activity') === 'yes' || Helper::isModerator();
365 }
366
367 public static function isCustomizationEnabled($key, $matchingValue = 'yes')
368 {
369 $settings = self::getCustomizationSettings();
370 return isset($settings[$key]) && $settings[$key] === $matchingValue;
371 }
372
373 public static function getProductUrl($isPowered = false, $params = [])
374 {
375 $url = 'https://fluentcommunity.co/';
376 if ($isPowered) {
377 $defaultParams = [
378 'utm_source' => 'power_footer',
379 'utm_medium' => 'site',
380 'utm_campaign' => 'powered_by'
381 ];
382 $params = wp_parse_args($params, $defaultParams);
383
384 $settings = self::getCustomizationSettings();
385 $affId = Arr::get($settings, 'affiliate_id');
386 if ($affId) {
387 $params['ref'] = $affId;
388 }
389
390 return add_query_arg($params, $url);
391 }
392
393 return add_query_arg([
394 'utm_source' => 'plugin',
395 'utm_medium' => 'site',
396 'utm_campaign' => 'plugin_ui'
397 ], $url);
398 }
399
400 /**
401 * Build a spec-compliant "Upgrade to Pro" URL.
402 *
403 * Follows the shared Fluent* UTM spec:
404 * utm_source = fluent-community (fixed vocabulary, never the wp.org slug)
405 * utm_medium = free_plugin | pro_plugin (acquisition vs cross-sell)
406 * utm_campaign= upgrade_pro (override for xsell_<target> / license_* )
407 * utm_content = the exact placement, e.g. feature_lock_moderation, upgrade_page
408 * utm_term = plugin version that generated the link
409 * utm_id = promo id, blank normally (omit unless passed)
410 *
411 * @param string $content The utm_content placement.
412 * @param array $overrides Override any utm_* param (e.g. utm_campaign for cross-sell).
413 * @return string
414 */
415 public static function getProUpgradeUrl($content = 'upgrade_page', $overrides = [])
416 {
417 $baseUrl = apply_filters(
418 'fluent_community/pro_upgrade_base_url',
419 'https://fluentcommunity.co/pricing/'
420 );
421
422 $params = wp_parse_args($overrides, [
423 'utm_source' => 'fluent-community',
424 'utm_medium' => defined('FLUENT_COMMUNITY_PRO_VERSION') ? 'pro_plugin' : 'free_plugin',
425 'utm_campaign' => 'upgrade_pro',
426 'utm_content' => $content,
427 'utm_term' => FLUENT_COMMUNITY_PLUGIN_VERSION,
428 ]);
429
430 // Drop any blank params (e.g. an unset utm_id) so they never hit the URL.
431 $params = array_filter($params, function ($value) {
432 return $value !== '' && $value !== null;
433 });
434
435 return add_query_arg($params, $baseUrl);
436 }
437
438 /**
439 * Get the email notification settings.
440 *
441 * @return array The email notification settings.
442 */
443 public static function getEmailNotificationSettings()
444 {
445 static $settings;
446 if ($settings) {
447 return $settings;
448 }
449
450 $default = [
451 'com_my_post_mail' => 'yes',
452 'reply_my_com_mail' => 'yes',
453 'mention_mail' => 'yes',
454 'digest_email_status' => 'no',
455 'digest_mail_day' => 'tue',
456 'daily_digest_time' => '09:00',
457 'send_from_email' => '',
458 'send_from_name' => '',
459 'reply_to_email' => '',
460 'reply_to_name' => '',
461 'email_footer' => 'You are getting this email because you are a member of {{site_name_with_url}}.' . PHP_EOL . PHP_EOL . '{{manage_email_notification_url|Manage Your Email Notifications Preference}}.',
462 'disable_powered_by' => 'no',
463 'logo' => ''
464 ];
465
466 $settings = array_filter(self::getOption('global_email_settings', $default));
467 $settings = wp_parse_args($settings, $default);
468
469 if (!defined('FLUENT_COMMUNITY_PRO')) {
470 $settings['disable_powered_by'] = 'no';
471 }
472
473 if (empty($settings['email_footer_rendered']) && !empty($settings['email_footer'])) {
474 $settings['email_footer_rendered'] = FeedsHelper::mdToHtml($settings['email_footer']);
475 }
476
477 return $settings;
478 }
479
480 public static function hasEmailAnnouncementEnabled()
481 {
482 $settings = self::getEmailNotificationSettings();
483 return Arr::get($settings, 'mention_mail', 'no') === 'yes';
484 }
485
486 public static function postTitlePref()
487 {
488 $pref = self::getCustomizationSetting('post_title_pref');
489
490 if ($pref == 'disabled') {
491 $pref = '';
492 }
493
494 return apply_filters('fluent_community/has_post_title', $pref);
495 }
496
497 public static function getSpaces($byGroups = false)
498 {
499 if ($byGroups) {
500 return SpaceGroup::orderBy('serial', 'ASC')->with('spaces', function ($q) {
501 $q->orderBy('serial', 'ASC')
502 ->where('type', 'community');
503 })->get();
504 }
505
506 return Space::where('type', 'community')->orderBy('serial', 'ASC')->get();
507 }
508
509 public static function getCourses($byGroups = false)
510 {
511 if ($byGroups) {
512 return SpaceGroup::orderBy('serial', 'ASC')->with('spaces', function ($q) {
513 $q->orderBy('serial', 'ASC')
514 ->where('type', 'course');
515 })->get();
516 }
517
518 return Course::orderBy('serial', 'ASC')->get();
519 }
520
521 public static function getTopics()
522 {
523 static $topics;
524 if ($topics) {
525 return $topics;
526 }
527
528 $key = 'fluent_community_post_topics';
529 $topics = self::getFromCache($key, function () {
530 $topics = Term::where('taxonomy_name', 'post_topic')->orderBy('title', 'ASC')->get();
531
532 /*
533 * object_id = term_id
534 * meta_key = space_id
535 */
536 $topicSpaceRelations = Meta::select(['id', 'object_id', 'meta_key'])->where('object_type', 'term_space_relation')
537 ->get();
538
539 $relations = [];
540 foreach ($topicSpaceRelations as $relation) {
541 if (!isset($relations[$relation->object_id])) {
542 $relations[$relation->object_id] = [];
543 }
544
545 $relations[$relation->object_id][] = $relation->meta_key;
546 }
547
548 $formattedTopics = [];
549 foreach ($topics as $topic) {
550 $formattedTopics[] = [
551 'id' => $topic->id,
552 'title' => $topic->title,
553 'description' => $topic->description,
554 'slug' => $topic->slug,
555 'admin_only' => Arr::get($topic->settings, 'admin_only', 'no'),
556 'space_ids' => isset($relations[$topic->id]) ? $relations[$topic->id] : []
557 ];
558 }
559
560 return $formattedTopics;
561 }, MONTH_IN_SECONDS);
562
563 return $topics;
564 }
565
566 public static function getTopicsBySpaceId($spaceId)
567 {
568 $topics = self::getTopics();
569 $topics = array_filter($topics, function ($topic) use ($spaceId) {
570 return in_array($spaceId, $topic['space_ids']);
571 });
572 return array_values($topics);
573 }
574
575 public static function getMaxRunTime()
576 {
577 if (function_exists('ini_get')) {
578 $maxRunTime = (int)ini_get('max_execution_time');
579 if ($maxRunTime === 0) {
580 $maxRunTime = 60;
581 }
582 // If set to 0 (unlimited) or a negative value, return a large number
583 if ($maxRunTime <= 0) {
584 return PHP_INT_MAX;
585 }
586
587 } else {
588 $maxRunTime = 30;
589 }
590
591 if ($maxRunTime > 58) {
592 $maxRunTime = 58;
593 }
594
595 $maxRunTime = $maxRunTime - 3;
596
597 return apply_filters('fluent_community/max_execution_time', $maxRunTime);
598 }
599
600 public static function getColorSchemas()
601 {
602 $defaultCustom = [
603 'title' => __('Custom', 'fluent-community'),
604 'selectors' => [
605 'body' => [],
606 'fcom_top_menu' => [],
607 'spaces' => []
608 ],
609 ];
610 $shemas = apply_filters('fluent-community/color_schemas', [
611 'lightSkins' => [
612 'default' => [
613 'title' => __('Default', 'fluent-community'),
614 'selectors' => [
615 'body' => [
616 'primary_bg' => '#ffffff',
617 'secondary_bg' => '#f0f2f5',
618 'secondary_content_bg' => '#f0f3f5',
619 'active_bg' => '#f0f3f5',
620 'light_bg' => '#E1E4EA',
621 'deep_bg' => '#222530',
622 'menu_text' => '#545861',
623 'primary_text' => '#19283a',
624 'secondary_text' => '#525866',
625 'text_off' => '#959595',
626 'primary_button' => '#2B2E33',
627 'primary_button_text' => '#ffffff',
628 'primary_border' => '#e3e8ee',
629 'secondary_border' => '#9CA3AF',
630 'highlight_bg' => '#fffce3',
631 'text_link' => '#2271b1',
632 ],
633 'fcom_top_menu' => [
634 'primary_bg' => '#ffffff',
635 'primary_border' => '#e3e8ee',
636 'menu_text' => '#545861',
637 'menu_text_active' => '#545861',
638 'active_bg' => '#f0f3f5',
639 'menu_text_hover' => '#545861',
640 'menu_bg_hover' => '#f0f3f5',
641 ],
642 'spaces' => [
643 'primary_bg' => '#ffffff',
644 'primary_border' => '#e3e8ee',
645 'menu_text' => '#545861',
646 'menu_text_active' => '#545861',
647 'menu_text_hover' => '#545861',
648 'menu_bg_hover' => '#f0f3f5',
649 'active_bg' => '#f0f3f5',
650 ]
651 ],
652 ],
653 'sunset_sands' => [
654 'title' => __('Sunset Sands', 'fluent-community'),
655 'selectors' => [
656 'body' => [
657 'primary_bg' => '#FFFFFF',
658 'secondary_bg' => '#FDF6F0',
659 'secondary_content_bg' => '#FDF6F0',
660 'active_bg' => '#FCF0E6',
661 'light_bg' => '#FAE5D3',
662 'deep_bg' => '#2D2D2D',
663 'menu_text' => '#5D5D5D',
664 'primary_text' => '#333333',
665 'secondary_text' => '#666666',
666 'text_off' => '#999999',
667 'text_link' => '#E67E22',
668 'primary_button' => '#E67E22',
669 'primary_button_text' => '#FFFFFF',
670 'primary_border' => '#EADDD3',
671 'secondary_border' => '#E0D0C3',
672 'highlight_bg' => '#FFF5EC',
673 ],
674 'fcom_top_menu' => [
675 'primary_bg' => '#FFFFFF',
676 'primary_border' => '#EADDD3',
677 'menu_text' => '#5D5D5D',
678 'menu_text_active' => '#E67E22',
679 'active_bg' => '#FFF5EC',
680 'menu_text_hover' => '#E67E22',
681 'menu_bg_hover' => '#FFF5EC',
682 ],
683 'spaces' => [
684 'primary_bg' => '#FFFFFF',
685 'primary_border' => '#EADDD3',
686 'menu_text' => '#5D5D5D',
687 'menu_text_hover' => '#E67E22',
688 'menu_bg_hover' => '#FFF5EC',
689 'menu_text_active' => '#FFFFFF',
690 'active_bg' => '#E67E22',
691 ]
692 ]
693 ],
694 'ocean_blue' => [
695 'title' => __('Ocean Blue', 'fluent-community'),
696 'selectors' => [
697 'body' => [
698 'primary_bg' => '#FFFFFF',
699 'secondary_bg' => '#F0F2F5',
700 'secondary_content_bg' => '#f0f3f5',
701 'active_bg' => '#E7F3FF',
702 'light_bg' => '#F6F9FA',
703 'deep_bg' => '#18191A',
704 'menu_text' => '#65676B',
705 'primary_text' => '#050505',
706 'secondary_text' => '#65676B',
707 'text_off' => '#8A8D91',
708 'text_link' => '#216FDB',
709 'primary_button' => '#1877F2',
710 'primary_button_text' => '#FFFFFF',
711 'primary_border' => '#DADDE1',
712 'secondary_border' => '#CED0D4',
713 'highlight_bg' => '#E7F3FF'
714 ],
715 'fcom_top_menu' => [
716 'primary_bg' => '#FFFFFF',
717 'primary_border' => '#DADDE1',
718 'menu_text' => '#65676B',
719 'menu_text_active' => '#1877F2',
720 'active_bg' => '#E7F3FF',
721 'menu_text_hover' => '#1877F2',
722 'menu_bg_hover' => '#E7F3FF',
723 ],
724 'spaces' => [
725 'primary_bg' => '#FFFFFF',
726 'primary_border' => '#DADDE1',
727 'menu_text' => '#65676B',
728 'menu_text_hover' => '#1877F2',
729 'menu_bg_hover' => '#E7F3FF',
730 'menu_text_active' => '#FFFFFF',
731 'active_bg' => '#1877F2'
732 ]
733 ]
734 ],
735 'sky_blue' => [
736 'title' => __('Sky Blue', 'fluent-community'),
737 'selectors' => [
738 'body' => [
739 'primary_bg' => '#FFFFFF',
740 'secondary_bg' => '#F7F9FA',
741 'secondary_content_bg' => '#f0f3f5',
742 'active_bg' => '#E8F5FD',
743 'light_bg' => '#F7F9FA',
744 'deep_bg' => '#15202B',
745 'menu_text' => '#536471',
746 'primary_text' => '#0F1419',
747 'secondary_text' => '#536471',
748 'text_off' => '#8899A6',
749 'text_link' => '#1D9BF0',
750 'primary_button' => '#1D9BF0',
751 'primary_button_text' => '#FFFFFF',
752 'primary_border' => '#EFF3F4',
753 'secondary_border' => '#CFD9DE',
754 'highlight_bg' => '#E8F5FD'
755 ],
756 'fcom_top_menu' => [
757 'primary_bg' => '#FFFFFF',
758 'primary_border' => '#EFF3F4',
759 'menu_text' => '#536471',
760 'menu_text_active' => '#1D9BF0',
761 'active_bg' => '#E8F5FD',
762 'menu_bg_hover' => '#E8F5FD',
763 'menu_text_hover' => '#536471',
764 ],
765 'spaces' => [
766 'primary_bg' => '#FFFFFF',
767 'primary_border' => '#EFF3F4',
768 'menu_text' => '#536471',
769 'menu_text_hover' => '#1D9BF0',
770 'menu_bg_hover' => '#E8F5FD',
771 'menu_text_active' => '#FFFFFF',
772 'active_bg' => '#1D9BF0'
773 ]
774 ]
775 ],
776 'emerald_essence' => [
777 'title' => __('Emerald Essence', 'fluent-community'),
778 'selectors' => [
779 'body' => [
780 'primary_bg' => '#FFFFFF',
781 'secondary_bg' => '#F0F7F4',
782 'secondary_content_bg' => '#f0f3f5',
783 'active_bg' => '#E3F2ED',
784 'light_bg' => '#F7FAFA',
785 'deep_bg' => '#1A2B32',
786 'menu_text' => '#4A5D5E',
787 'primary_text' => '#1F2937',
788 'secondary_text' => '#4B5563',
789 'text_off' => '#9CA3AF',
790 'text_link' => '#059669',
791 'primary_button' => '#10B981',
792 'primary_button_text' => '#FFFFFF',
793 'primary_border' => '#D1E7DD',
794 'secondary_border' => '#A7C4BC',
795 'highlight_bg' => '#ECFDF5'
796 ],
797 'fcom_top_menu' => [
798 'primary_bg' => '#FFFFFF',
799 'primary_border' => '#D1E7DD',
800 'menu_text' => '#4A5D5E',
801 'menu_text_active' => '#059669',
802 'active_bg' => '#ECFDF5',
803 'menu_bg_hover' => '#ECFDF5',
804 'menu_text_hover' => '#4A5D5E',
805 ],
806 'spaces' => [
807 'primary_bg' => '#FFFFFF',
808 'primary_border' => '#D1E7DD',
809 'menu_text' => '#4A5D5E',
810 'menu_text_hover' => '#059669',
811 'menu_bg_hover' => '#ECFDF5',
812 'menu_text_active' => '#FFFFFF',
813 'active_bg' => '#10B981'
814 ]
815 ]
816 ]
817 ],
818 'darkSkins' => [
819 'default' => [
820 'title' => __('Default (Dark)', 'fluent-community'),
821 'selectors' => [
822 'body' => [
823 'primary_bg' => '#2B2E33',
824 'secondary_bg' => '#191B1F',
825 'secondary_content_bg' => '#42464D',
826 'active_bg' => '#42464D',
827 'light_bg' => '#2B303B',
828 'deep_bg' => '#E1E4EA',
829 'menu_text' => '#E4E7EB',
830 'menu_text_active' => '#E4E7EB',
831 'primary_text' => '#F0F3F5',
832 'secondary_text' => '#99A0AE',
833 'menu_bg_hover' => '#E1E4EA',
834 'text_off' => '#A5A9AD',
835 'text_link' => '#60a5fa',
836 'primary_button' => '#FFFFFF',
837 'primary_button_text' => '#2B2E33',
838 'primary_border' => '#42464D',
839 'secondary_border' => '#A5A9AD',
840 'highlight_bg' => '#2c2c1a',
841 ],
842 'fcom_top_menu' => [
843 'primary_bg' => '#2B2E33',
844 'primary_border' => '#42464D',
845 'menu_text' => '#E4E7EB',
846 'menu_text_active' => '#E4E7EB',
847 'active_bg' => '#42464D',
848 'menu_bg_hover' => '#42464D',
849 'menu_text_hover' => '#E4E7EB',
850 ],
851 'spaces' => [
852 'primary_bg' => '#2B2E33',
853 'primary_border' => '#42464D',
854 'menu_text' => '#E4E7EB',
855 'menu_text_active' => '#E4E7EB',
856 'active_bg' => '#42464D',
857 'menu_bg_hover' => '#42464D',
858 'menu_text_hover' => '#fff',
859 ]
860 ],
861 ],
862 'sunset_sands' => [
863 'title' => __('Sunset Sands (Dark)', 'fluent-community'),
864 'selectors' => [
865 'body' => [
866 'primary_bg' => '#1A1A1A',
867 'secondary_bg' => '#222222',
868 'secondary_content_bg' => '#222222',
869 'active_bg' => '#2A2420',
870 'light_bg' => '#33302E',
871 'deep_bg' => '#111111',
872 'menu_text' => '#B0B0B0',
873 'primary_text' => '#E0E0E0',
874 'secondary_text' => '#A0A0A0',
875 'text_off' => '#707070',
876 'text_link' => '#F39C12',
877 'primary_button' => '#F39C12',
878 'primary_border' => '#3A3632',
879 'primary_button_text' => '#1A1A1A',
880 'secondary_border' => '#4C4D4F',
881 'highlight_bg' => '#2A2420',
882 ],
883 'fcom_top_menu' => [
884 'primary_bg' => '#1A1A1A',
885 'primary_border' => '#3A3632',
886 'menu_text' => '#B0B0B0',
887 'menu_text_active' => '#F39C12',
888 'active_bg' => '#2A2420',
889 'menu_text_hover' => '#F39C12',
890 'menu_bg_hover' => '#2A2420',
891 ],
892 'spaces' => [
893 'primary_bg' => '#1A1A1A',
894 'primary_border' => '#3A3632',
895 'menu_text' => '#B0B0B0',
896 'menu_text_hover' => '#F39C12',
897 'menu_bg_hover' => '#2A2420',
898 'menu_text_active' => '#1A1A1A',
899 'active_bg' => '#F39C12',
900 ]
901 ]
902 ],
903 'ocean_blue' => [
904 'title' => __('Ocean Blue (Dark)', 'fluent-community'),
905 'selectors' => [
906 'body' => [
907 'primary_border' => '#3E4042',
908 'menu_text' => '#B0B3B8',
909 'menu_text_active' => '#2D88FF',
910 'active_bg' => '#263951',
911 'menu_text_hover' => '#2D88FF',
912 'menu_bg_hover' => '#263951',
913 'primary_bg' => '#2B2E33',
914 'secondary_content_bg' => '#42464D',
915 'secondary_bg' => '#191B1F',
916 'light_bg' => '#2B303B',
917 'deep_bg' => '#E1E4EA',
918 'primary_text' => '#F0F3F5',
919 'secondary_text' => '#99A0AE',
920 'text_off' => '#A5A9AD',
921 'primary_button' => '#FFFFFF',
922 'primary_button_text' => '#191B1F',
923 'secondary_border' => '#A5A9AD',
924 'highlight_bg' => '#2c2c1a',
925 'text_link' => '#2d88ff',
926 ],
927 'fcom_top_menu' => [
928 'primary_bg' => '#242526',
929 'primary_border' => '#3E4042',
930 'menu_text' => '#B0B3B8',
931 'menu_text_active' => '#fff',
932 'active_bg' => '#263951',
933 'menu_text_hover' => '#fff',
934 'menu_bg_hover' => '#263951',
935 ],
936 'spaces' => [
937 'primary_bg' => '#242526',
938 'primary_border' => '#3E4042',
939 'menu_text' => '#B0B3B8',
940 'menu_text_hover' => '#2D88FF',
941 'menu_bg_hover' => '#263951',
942 'menu_text_active' => '#FFFFFF',
943 'active_bg' => '#2D88FF'
944 ]
945 ]
946 ],
947 'sky_blue' => [
948 'title' => __('Sky Blue (Dark)', 'fluent-community'),
949 'selectors' => [
950 'body' => [
951 'primary_bg' => '#15202B',
952 'secondary_bg' => '#1E2732',
953 'secondary_content_bg' => '#2C3640',
954 'active_bg' => '#1D2F41',
955 'light_bg' => '#22303C',
956 'deep_bg' => '#E7E9EA',
957 'menu_text' => '#8B98A5',
958 'primary_text' => '#E7E9EA',
959 'secondary_text' => '#8B98A5',
960 'text_off' => '#536471',
961 'text_link' => '#1D9BF0',
962 'primary_button' => '#1D9BF0',
963 'primary_button_text' => '#15202B',
964 'primary_border' => '#38444D',
965 'secondary_border' => '#536471',
966 'highlight_bg' => '#1D2F41'
967 ],
968 'fcom_top_menu' => [
969 'primary_bg' => '#15202B',
970 'primary_border' => '#38444D',
971 'menu_text' => '#8B98A5',
972 'menu_text_active' => '#1D9BF0',
973 'active_bg' => '#1D2F41',
974 'menu_bg_hover' => '#1D2F41',
975 'menu_text_hover' => '#E7E9EA',
976 ],
977 'spaces' => [
978 'primary_bg' => '#15202B',
979 'primary_border' => '#38444D',
980 'menu_text' => '#8B98A5',
981 'menu_text_hover' => '#1D9BF0',
982 'menu_bg_hover' => '#1D2F41',
983 'menu_text_active' => '#FFFFFF',
984 'active_bg' => '#1D9BF0'
985 ]
986 ]
987 ],
988 'emerald_essence' => [
989 'title' => __('Emerald Essence (Dark)', 'fluent-community'),
990 'selectors' => [
991 'body' => [
992 'primary_bg' => '#1A2B32',
993 'secondary_bg' => '#243B43',
994 'secondary_content_bg' => '#2C464F',
995 'active_bg' => '#1E3A31',
996 'light_bg' => '#2A3F48',
997 'deep_bg' => '#E2E8F0',
998 'menu_text' => '#A7BCBF',
999 'primary_text' => '#E2E8F0',
1000 'secondary_text' => '#A7BCBF',
1001 'text_off' => '#64748B',
1002 'text_link' => '#34D399',
1003 'primary_button' => '#10B981',
1004 'primary_border' => '#2F4C41',
1005 'primary_button_text' => '#1A2B32',
1006 'secondary_border' => '#3D5C52',
1007 'highlight_bg' => '#064E3B'
1008 ],
1009 'fcom_top_menu' => [
1010 'primary_bg' => '#1A2B32',
1011 'primary_border' => '#2F4C41',
1012 'menu_text' => '#A7BCBF',
1013 'menu_text_active' => '#34D399',
1014 'active_bg' => '#064E3B',
1015 'menu_bg_hover' => '#064E3B',
1016 'menu_text_hover' => '#E2E8F0',
1017 ],
1018 'spaces' => [
1019 'primary_bg' => '#1A2B32',
1020 'primary_border' => '#2F4C41',
1021 'menu_text' => '#A7BCBF',
1022 'menu_text_hover' => '#34D399',
1023 'menu_bg_hover' => '#064E3B',
1024 'menu_text_active' => '#FFFFFF',
1025 'active_bg' => '#10B981'
1026 ]
1027 ]
1028 ]
1029 ]
1030 ]);
1031
1032 $customSchemaConfig = self::getColorConfig('edit');
1033
1034 $shemas['lightSkins']['custom'] = [
1035 'title' => __('Custom', 'fluent-community'),
1036 'selectors' => $customSchemaConfig['light_config'] ?? $defaultCustom
1037 ];
1038
1039 $shemas['darkSkins']['custom'] = [
1040 'title' => __('Custom', 'fluent-community'),
1041 'selectors' => $customSchemaConfig['dark_config'] ?? $defaultCustom
1042 ];
1043
1044 return $shemas;
1045 }
1046
1047 public static function generateCss($sectors, $prefix = '')
1048 {
1049 if (!$sectors) {
1050 return '';
1051 }
1052
1053 $bodyPrefix = 'body';
1054 if ($prefix) {
1055 $bodyPrefix = $prefix . ' body';
1056 }
1057
1058 $elementPlusMap = [
1059 'primary_text' => '--el-color-primary',
1060 'secondary_bg' => '--el-color-white'
1061 ];
1062
1063 $css = '';
1064 foreach ($sectors as $selector => $props) {
1065 $isBody = $selector === 'body';
1066 $prefix = $isBody ? $bodyPrefix : $bodyPrefix . ' .' . $selector;
1067 $prefix .= '{';
1068
1069 $css .= $prefix;
1070
1071 foreach ($props as $prop => $value) {
1072 $cssVar = '--fcom-' . str_replace('_', '-', $prop);
1073 $css .= $cssVar . ':' . $value . ';';
1074
1075 if ($isBody && isset($elementPlusMap[$prop])) {
1076 $css .= $elementPlusMap[$prop] . ':' . $value . ';';
1077 }
1078 }
1079 $css .= '} ';
1080 }
1081
1082 return $css;
1083 }
1084
1085 public static function getColorConfig($context = 'view')
1086 {
1087 return apply_filters('fluent_community/color_schmea_config', [
1088 'light_schema' => 'default',
1089 'dark_schema' => 'default',
1090 'light_config' => [
1091 'body' => [],
1092 'fcom_top_menu' => [],
1093 'spaces' => []
1094 ],
1095 'dark_config' => [
1096 'body' => [],
1097 'fcom_top_menu' => [],
1098 'spaces' => []
1099 ],
1100 'version' => FLUENT_COMMUNITY_PLUGIN_VERSION
1101 ], $context);
1102 }
1103
1104 public static function getColorCssVariables()
1105 {
1106 $customSchemaConfig = self::getColorConfig('view');
1107
1108 if (!empty($customSchemaConfig['cached_css'])) {
1109 if ($customSchemaConfig['version'] != FLUENT_COMMUNITY_PLUGIN_VERSION) {
1110 do_action('fluent_community/recache_color_schema');
1111 }
1112
1113 return (string)$customSchemaConfig['cached_css'];
1114 }
1115
1116 $schemas = self::getColorSchemas();
1117
1118 $lightName = Arr::get($customSchemaConfig, 'light_schema', 'default');
1119 $darkName = Arr::get($customSchemaConfig, 'dark_schema', 'default');
1120
1121 $lightCss = self::generateCss(Arr::get($schemas, "lightSkins.$lightName.selectors"));
1122 $darkCss = self::generateCss(Arr::get($schemas, "darkSkins.$darkName.selectors"), 'html.dark');
1123 return $lightCss . $darkCss;
1124 }
1125
1126 public static function getThemeColor($scope = 'theme')
1127 {
1128 static $cache = [];
1129 if (isset($cache[$scope])) {
1130 return $cache[$scope];
1131 }
1132
1133 $map = [
1134 'theme' => ['key' => 'primary_button', 'default' => '#2B2E33'],
1135 'theme_button_text' => ['key' => 'primary_button_text', 'default' => '#ffffff'],
1136 ];
1137 $entry = Arr::get($map, $scope, ['key' => $scope, 'default' => '']);
1138
1139 $schemas = self::getColorSchemas();
1140 $lightName = Arr::get(self::getColorConfig('view'), 'light_schema', 'default');
1141 $color = Arr::get($schemas, "lightSkins.$lightName.selectors.body.{$entry['key']}", $entry['default']);
1142
1143 $cache[$scope] = apply_filters('fluent_community/' . $scope . '_color', $color);
1144
1145 return $cache[$scope];
1146 }
1147
1148 public static function getColorSchemaConfig()
1149 {
1150 $customSchemaConfig = self::getColorConfig('view');
1151 $lightName = Arr::get($customSchemaConfig, 'light_schema', 'default');
1152 $darkName = Arr::get($customSchemaConfig, 'dark_schema', 'default');
1153 $schemas = self::getColorSchemas();
1154
1155 return [
1156 'dark' => Arr::get($schemas, "lightSkins.$darkName.selectors"),
1157 'light' => Arr::get($schemas, "darkSkins.$lightName.selectors"),
1158 ];
1159 }
1160
1161 public static function getSuggestedColors()
1162 {
1163 $pallets = current((array)get_theme_support('editor-color-palette'));
1164
1165 $colors = [];
1166
1167 if ($pallets && is_array($pallets)) {
1168 $colors = array_map(function ($color) {
1169 $colorString = Arr::get($color, 'color');
1170 // Trim any whitespace
1171 $colorString = trim($colorString);
1172 // Check if it's a CSS variable
1173 if (strpos($colorString, 'var(') === 0) {
1174 // Extract the fallback color if present
1175 preg_match('/var\(.*,\s*(#[A-Fa-f0-9]{6})\)/', $colorString, $matches);
1176 if (!empty($matches[1])) {
1177 return $matches[1];
1178 }
1179 // If no fallback color, return an empty string
1180 return '';
1181 }
1182
1183 if (preg_match('/#[A-Fa-f0-9]{6}/', $colorString, $matches)) {
1184 return $matches[0];
1185 }
1186
1187 return $colorString;
1188 }, $pallets);
1189
1190 $colors = array_filter($colors);
1191 }
1192
1193 if (!$colors) {
1194 $colors = ['#000000', '#abb8c3', '#ffffff', '#f78da7', '#ff6900', '#fcb900', '#7bdcb5', '#00d084', '#8ed1fc', '#0693e3', '#9b51e0'];
1195 }
1196
1197 return apply_filters('fluent_community/suggested_colors', $colors);
1198 }
1199
1200 public static function slugify($text, $fallback = '')
1201 {
1202 $title = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $text);
1203
1204 $title = Helper::normalizeToAscii($title);
1205
1206 $title = remove_accents($title);
1207
1208 $title = strtolower($title);
1209 // only allow alphanumeric, dash, and underscore
1210 $title = trim(preg_replace('/[^a-z0-9-_]/', ' ', $title));
1211
1212 if (!$title) {
1213 $title = $fallback;
1214 }
1215
1216 if (!$title) {
1217 return $title;
1218 }
1219
1220 return sanitize_title($title, $fallback);
1221 }
1222
1223 public static function hasAnalyticsEnabled()
1224 {
1225 $defaultSettings = ['status' => 'no'];
1226
1227 $settings = apply_filters('fluent_community/features/analytics', $defaultSettings);
1228
1229 $status = Arr::get($settings, 'status');
1230
1231 return $status === 'yes';
1232 }
1233
1234 public static function getPortalSidebarData($scope = 'sidebar')
1235 {
1236 $userModel = Helper::getCurrentUser();
1237 $spaceGroups = Helper::getCommunityMenuGroups($userModel);
1238 $settingsMenu = apply_filters('fluent_community/settings_menu', [], $userModel);
1239 $menuGroups = Helper::getMenuItemsGroup('view');
1240 $topInlines = Arr::get($menuGroups, 'beforeCommunityMenuItems', []);
1241 $bottomLinkGroups = Arr::get($menuGroups, 'afterCommunityLinkGroups', []);
1242 $primaryMenuItems = Arr::get($menuGroups, 'mainMenuItems', []);
1243 $primaryMenuItems = apply_filters('fluent_community/main_menu_items', $primaryMenuItems, $scope);
1244
1245 return apply_filters('fluent_community/sidebar_menu_groups_config', [
1246 'primaryItems' => $primaryMenuItems,
1247 'spaceGroups' => $spaceGroups,
1248 'settingsItems' => $settingsMenu,
1249 'topInlineLinks' => $topInlines,
1250 'bottomLinkGroups' => $bottomLinkGroups,
1251 'is_admin' => Helper::isSiteAdmin(null, $userModel),
1252 'has_color_scheme' => Helper::hasColorScheme(),
1253 'context' => $scope,
1254 ], $userModel);
1255 }
1256
1257 public static function getVerifiedSenders()
1258 {
1259 $verifiedSenders = [];
1260
1261 if (defined('FLUENTMAIL')) {
1262 $smtpSettings = get_option('fluentmail-settings', []);
1263 if ($smtpSettings && count($smtpSettings['mappings'])) {
1264 $verifiedSenders = array_keys($smtpSettings['mappings']);
1265 }
1266 }
1267
1268 return apply_filters('fluent_community/verified_email_senders', $verifiedSenders);
1269 }
1270
1271 public static function safeUnserialize($data)
1272 {
1273 if (!$data) {
1274 return $data;
1275 }
1276
1277 if (is_serialized($data)) { // Don't attempt to unserialize data that wasn't serialized going in.
1278 return @unserialize(trim($data), [
1279 'allowed_classes' => false,
1280 ]);
1281 }
1282
1283 return $data;
1284 }
1285 }
1286