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

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