PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.01
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.10.01, at app/Functions/Utility.php

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