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

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