PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.6.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.6.01
2.11.0 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 All 78 releases
fluent-community / app / Hooks / Handlers / PortalHandler.php

PortalHandler.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.6.01, at app/Hooks/Handlers/PortalHandler.php

1,294 lines 68.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\Hooks\Handlers;
4
5 use FluentCommunity\App\App;
6 use FluentCommunity\App\Functions\Utility;
7 use FluentCommunity\App\Models\Space;
8 use FluentCommunity\App\Models\Feed;
9 use FluentCommunity\App\Models\BaseSpace;
10 use FluentCommunity\App\Models\SpaceGroup;
11 use FluentCommunity\App\Models\User;
12 use FluentCommunity\App\Models\XProfile;
13 use FluentCommunity\App\Services\CustomSanitizer;
14 use FluentCommunity\App\Services\Helper;
15 use FluentCommunity\App\Services\ProfileHelper;
16 use FluentCommunity\App\Services\TransStrings;
17 use FluentCommunity\App\Vite;
18 use FluentCommunity\App\Models\NotificationSubscriber;
19 use FluentCommunity\Framework\Support\Arr;
20 use FluentCommunity\App\Services\FeedsHelper;
21 use FluentCommunity\Modules\Auth\AuthHelper;
22 use FluentCommunity\Modules\Course\Model\CourseLesson;
23
24 class PortalHandler
25 {
26 protected $currentPath = '';
27
28 protected $slug = null;
29
30 public function register()
31 {
32 /**
33 * Register the portal route
34 */
35 add_action('init', function () {
36 $this->slug = Helper::getPortalSlug();
37 if ($this->slug !== '') {
38 add_rewrite_rule('^' . $this->slug . '/?$', 'index.php?fcom_route=portal_home', 'top'); // For /hooks
39 add_rewrite_rule('^' . $this->slug . '/(.+)/?', 'index.php?fcom_route=$matches[1]', 'top');
40 }
41 });
42
43 add_filter('query_vars', function ($vars) {
44 $vars[] = 'fcom_route';
45 return $vars;
46 });
47
48 add_action('template_redirect', function () {
49 $hook_path = get_query_var('fcom_route');
50
51 if ($hook_path) {
52 $this->currentPath = $hook_path;
53 $this->renderFullApp();
54 }
55
56 if ($this->slug === '') {
57 global $wp;
58 $currentRequest = $wp->request;
59 if ($hookPath = Helper::getPortalRequestPath($currentRequest)) {
60 $this->currentPath = $hookPath;
61 $this->renderFullApp();
62 }
63 }
64 }, 1);
65
66 add_action('fluent_community/portal_html', function () {
67 App::make('view')->render('portal.portal');
68 });
69
70 add_action('fluent_community/portal_header', function ($context) {
71 $this->getPortalHeader(true, $context);
72 });
73
74 add_action('fluent_community/portal_sidebar', function ($context) {
75 $this->getPortalSidebar(true, $context);
76 });
77
78 add_action('fluent_community/enqueue_global_assets', function ($useDefaultTheme = true) {
79 $isRtl = Helper::isRtl();
80
81 wp_enqueue_style('fluent_community_global', Vite::getStaticSrcUrl('global.css', $isRtl), [], FLUENT_COMMUNITY_PLUGIN_VERSION, 'screen');
82
83 if ($useDefaultTheme) {
84 wp_enqueue_style('fluent_community_default_theme', Vite::getStaticSrcUrl('theme-default.css', $isRtl), [], FLUENT_COMMUNITY_PLUGIN_VERSION, 'screen');
85 }
86 $css = Utility::getColorCssVariables();
87 wp_add_inline_style('fluent_community_global', $css);
88 wp_enqueue_script('portal_general', Vite::getStaticSrcUrl('portal_general.js'), [], FLUENT_COMMUNITY_PLUGIN_VERSION, [
89 'in_footer' => true
90 ]);
91 wp_localize_script('portal_general', 'fcom_portal_general', $this->getGlobalScriptVars());
92 });
93
94 add_action('admin_bar_menu', function ($wp_admin_bar) {
95 if (!\FluentCommunity\App\Services\Helper::canAccessPortal()) {
96 return;
97 }
98
99 // Add custom link within site name dropdown
100 $wp_admin_bar->add_node(array(
101 'id' => 'fluent-community-link',
102 'title' => __('Visit Community Portal', 'fluent-community'),
103 'href' => \FluentCommunity\App\Services\Helper::baseUrl('/'),
104 'parent' => 'site-name'
105 ));
106 }, 99);
107
108 add_action('fluent_community/top_menu_right_items', [$this, 'renderTopMenuRightItems']);
109
110 add_action('fluent_community/after_portal_sidebar', function () {
111
112 if (did_action('fluent_community/after_header_right_menu_items')) {
113 echo '<div role="region" aria-label="Portal Settings" class="fcom_side_footer">';
114 if (Helper::isSiteAdmin()) {
115 ?>
116 <div style="display: flex;justify-content: space-between;" class="fcom_admin_menu">
117 <?php if (!defined('FLUENT_COMMUNITY_PRO')): ?>
118 <a title="Upgrade to Pro" target="_blank" rel="noopener" class="fcom_inline_icon_link_item"
119 href="<?php echo esc_url(Utility::getProductUrl(true)) ?>">
120 <span class="el-icon">
121 <svg width="126" height="125" viewBox="0 0 126 125" fill="none"><rect x="0.22139"
122 width="125"
123 height="125"
124 rx="17.8571"
125 fill="#4A5FE0"/>
126 <path
127 d="M47.9424 75.1832L61.6888 67.2467L69.6253 80.9931C62.0334 85.3763 52.3256 82.7751 47.9424 75.1832Z"
128 fill="white"/>
129 <path fill-rule="evenodd" clip-rule="evenodd"
130 d="M75.4348 59.3101L61.6884 67.2466L69.6249 80.993L83.3713 73.0565L75.4348 59.3101ZM89.1821 51.3734L75.4356 59.3099L83.3721 73.0564L97.1186 65.1199L89.1821 51.3734Z"
131 fill="white"/>
132 <path
133 d="M89.182 51.3736C92.978 49.182 97.8319 50.4826 100.023 54.2786L103.992 61.1518L97.1185 65.12L89.182 51.3736Z"
134 fill="white"/>
135 <path
136 d="M64.593 56.4052L50.8466 64.3417L42.9101 50.5953L56.6565 42.6588L64.593 56.4052Z"
137 fill="white"/>
138 <path
139 d="M78.3397 48.4683L64.5933 56.4048L56.6568 42.6584C64.2487 38.2752 73.9565 40.8764 78.3397 48.4683Z"
140 fill="white"/>
141 <path
142 d="M50.847 64.3418L37.1006 72.2783L29.1641 58.5318L42.9105 50.5953L50.847 64.3418Z"
143 fill="white"/>
144 <path
145 d="M37.1011 72.2783C33.3051 74.4699 28.4512 73.1693 26.2596 69.3733L22.2913 62.5001L29.1646 58.5319L37.1011 72.2783Z"
146 fill="white"/>
147 </svg>
148 </span>
149 <span><?php esc_html_e('Upgrade', 'fluent-community'); ?></span>
150 </a>
151 <?php else: ?>
152 <a title="Go to /wp-admin" class="fcom_inline_icon_link_item fcom_wp_admin_link"
153 href="<?php echo esc_url(admin_url()); ?>">
154 <span class="el-icon">
155 <svg viewBox="0 0 122.52 122.523"><g fill="currentColor">
156 <path
157 d="m8.708 61.26c0 20.802 12.089 38.779 29.619 47.298l-25.069-68.686c-2.916 6.536-4.55 13.769-4.55 21.388z"/>
158 <path
159 d="m96.74 58.608c0-6.495-2.333-10.993-4.334-14.494-2.664-4.329-5.161-7.995-5.161-12.324 0-4.831 3.664-9.328 8.825-9.328.233 0 .454.029.681.042-9.35-8.566-21.807-13.796-35.489-13.796-18.36 0-34.513 9.42-43.91 23.688 1.233.037 2.395.063 3.382.063 5.497 0 14.006-.667 14.006-.667 2.833-.167 3.167 3.994.337 4.329 0 0-2.847.335-6.015.501l19.138 56.925 11.501-34.493-8.188-22.434c-2.83-.166-5.511-.501-5.511-.501-2.832-.166-2.5-4.496.332-4.329 0 0 8.679.667 13.843.667 5.496 0 14.006-.667 14.006-.667 2.835-.167 3.168 3.994.337 4.329 0 0-2.853.335-6.015.501l18.992 56.494 5.242-17.517c2.272-7.269 4.001-12.49 4.001-16.989z"/>
160 <path
161 d="m62.184 65.857-15.768 45.819c4.708 1.384 9.687 2.141 14.846 2.141 6.12 0 11.989-1.058 17.452-2.979-.141-.225-.269-.464-.374-.724z"/>
162 <path
163 d="m107.376 36.046c.226 1.674.354 3.471.354 5.404 0 5.333-.996 11.328-3.996 18.824l-16.053 46.413c15.624-9.111 26.133-26.038 26.133-45.426.001-9.137-2.333-17.729-6.438-25.215z"/>
164 <path
165 d="m61.262 0c-33.779 0-61.262 27.481-61.262 61.26 0 33.783 27.483 61.263 61.262 61.263 33.778 0 61.265-27.48 61.265-61.263-.001-33.779-27.487-61.26-61.265-61.26zm0 119.715c-32.23 0-58.453-26.223-58.453-58.455 0-32.23 26.222-58.451 58.453-58.451 32.229 0 58.45 26.221 58.45 58.451 0 32.232-26.221 58.455-58.45 58.455z"/></g>
166 </svg>
167 </span>
168 </a>
169 <?php endif; ?>
170 <a title="Portal Settings" class="fcom_inline_icon_link_item"
171 href="<?php echo esc_url(Helper::baseUrl('admin/settings')); ?>">
172 <span class="el-icon">
173 <svg viewBox="0 0 1024 1024" data-v-d2e47025="">
174 <path fill="currentColor"
175 d="M600.704 64a32 32 0 0 1 30.464 22.208l35.2 109.376c14.784 7.232 28.928 15.36 42.432 24.512l112.384-24.192a32 32 0 0 1 34.432 15.36L944.32 364.8a32 32 0 0 1-4.032 37.504l-77.12 85.12a357.12 357.12 0 0 1 0 49.024l77.12 85.248a32 32 0 0 1 4.032 37.504l-88.704 153.6a32 32 0 0 1-34.432 15.296L708.8 803.904c-13.44 9.088-27.648 17.28-42.368 24.512l-35.264 109.376A32 32 0 0 1 600.704 960H423.296a32 32 0 0 1-30.464-22.208L357.696 828.48a351.616 351.616 0 0 1-42.56-24.64l-112.32 24.256a32 32 0 0 1-34.432-15.36L79.68 659.2a32 32 0 0 1 4.032-37.504l77.12-85.248a357.12 357.12 0 0 1 0-48.896l-77.12-85.248A32 32 0 0 1 79.68 364.8l88.704-153.6a32 32 0 0 1 34.432-15.296l112.32 24.256c13.568-9.152 27.776-17.408 42.56-24.64l35.2-109.312A32 32 0 0 1 423.232 64H600.64zm-23.424 64H446.72l-36.352 113.088-24.512 11.968a294.113 294.113 0 0 0-34.816 20.096l-22.656 15.36-116.224-25.088-65.28 113.152 79.68 88.192-1.92 27.136a293.12 293.12 0 0 0 0 40.192l1.92 27.136-79.808 88.192 65.344 113.152 116.224-25.024 22.656 15.296a294.113 294.113 0 0 0 34.816 20.096l24.512 11.968L446.72 896h130.688l36.48-113.152 24.448-11.904a288.282 288.282 0 0 0 34.752-20.096l22.592-15.296 116.288 25.024 65.28-113.152-79.744-88.192 1.92-27.136a293.12 293.12 0 0 0 0-40.256l-1.92-27.136 79.808-88.128-65.344-113.152-116.288 24.96-22.592-15.232a287.616 287.616 0 0 0-34.752-20.096l-24.448-11.904L577.344 128zM512 320a192 192 0 1 1 0 384 192 192 0 0 1 0-384m0 64a128 128 0 1 0 0 256 128 128 0 0 0 0-256"></path>
176 </svg>
177 </span>
178 <span><?php esc_html_e('Settings', 'fluent-community'); ?></span>
179 </a>
180 </div>
181 <?php
182 } else if (Utility::isCustomizationEnabled('show_powered_by')) {
183 ?>
184 <a target="_blank" rel="noopener" style="font-size: 80%; cursor: pointer;"
185 class="fcom_inline_icon_link_item" href="<?php echo esc_url(Utility::getProductUrl(true)) ?>">
186 <?php echo esc_html__('Powered by FluentCommunity', 'fluent-community'); ?>
187 </a>
188 <?php
189 }
190 echo '</div>';
191 return;
192 }
193
194 if (Helper::isSiteAdmin()) {
195 add_action('fluent_community/before_header_menu_items', function () {
196 ?>
197 <li class="top_menu_item fcom_icon_link">
198 <a href="<?php echo esc_url(Helper::baseUrl('admin/settings')); ?>">
199 <span class="el-icon">
200 <svg viewBox="0 0 1024 1024" data-v-d2e47025="">
201 <path fill="currentColor"
202 d="M600.704 64a32 32 0 0 1 30.464 22.208l35.2 109.376c14.784 7.232 28.928 15.36 42.432 24.512l112.384-24.192a32 32 0 0 1 34.432 15.36L944.32 364.8a32 32 0 0 1-4.032 37.504l-77.12 85.12a357.12 357.12 0 0 1 0 49.024l77.12 85.248a32 32 0 0 1 4.032 37.504l-88.704 153.6a32 32 0 0 1-34.432 15.296L708.8 803.904c-13.44 9.088-27.648 17.28-42.368 24.512l-35.264 109.376A32 32 0 0 1 600.704 960H423.296a32 32 0 0 1-30.464-22.208L357.696 828.48a351.616 351.616 0 0 1-42.56-24.64l-112.32 24.256a32 32 0 0 1-34.432-15.36L79.68 659.2a32 32 0 0 1 4.032-37.504l77.12-85.248a357.12 357.12 0 0 1 0-48.896l-77.12-85.248A32 32 0 0 1 79.68 364.8l88.704-153.6a32 32 0 0 1 34.432-15.296l112.32 24.256c13.568-9.152 27.776-17.408 42.56-24.64l35.2-109.312A32 32 0 0 1 423.232 64H600.64zm-23.424 64H446.72l-36.352 113.088-24.512 11.968a294.113 294.113 0 0 0-34.816 20.096l-22.656 15.36-116.224-25.088-65.28 113.152 79.68 88.192-1.92 27.136a293.12 293.12 0 0 0 0 40.192l1.92 27.136-79.808 88.192 65.344 113.152 116.224-25.024 22.656 15.296a294.113 294.113 0 0 0 34.816 20.096l24.512 11.968L446.72 896h130.688l36.48-113.152 24.448-11.904a288.282 288.282 0 0 0 34.752-20.096l22.592-15.296 116.288 25.024 65.28-113.152-79.744-88.192 1.92-27.136a293.12 293.12 0 0 0 0-40.256l-1.92-27.136 79.808-88.128-65.344-113.152-116.288 24.96-22.592-15.232a287.616 287.616 0 0 0-34.752-20.096l-24.448-11.904L577.344 128zM512 320a192 192 0 1 1 0 384 192 192 0 0 1 0-384m0 64a128 128 0 1 0 0 256 128 128 0 0 0 0-256"></path>
203 </svg>
204 </a>
205 </li>
206 <?php
207 });
208 }
209
210 // echo '<div class="fcom_side_footer">';
211 // $this->renderTopMenuRightItems();
212 // echo '</div>';
213 });
214
215 add_action('fluent_community/portal_action_reactivate_account', function ($data) {
216 $xprofile = Helper::getCurrentProfile();
217
218 if ($xprofile && $xprofile->status == '' && Utility::getPrivacySetting('can_deactive_account') === 'yes') {
219
220 $nonce = Arr::get($data, '__nonce', '');
221 if (!wp_verify_nonce($nonce, 'fcom_reactivate_account')) {
222 $this->viewErrorPage(__('Invalid request', 'fluent-community'), __('Security check failed. Please try again.', 'fluent-community'));
223 }
224
225 // we need to reactivate the account
226 $xprofile->status = 'active';
227 $xprofile->save();
228 do_action('fluent_community/reactivate_account', $xprofile);
229 delete_user_meta($xprofile->user_id, '_fcom_deactivated_at');
230 }
231
232 // redirect to the portal home
233 wp_safe_redirect(Helper::baseUrl('/'));
234 exit;
235 });
236 }
237
238 public function renderTopMenuRightItems($context = 'headless')
239 {
240 $auth_url = $this->getAuthUrl();
241 $auth = Helper::getCurrentProfile();
242 $has_color_scheme = Helper::hasColorScheme();
243 $profileLinks = $this->getProfileLinks($auth);
244 ?>
245 <ul class="fcom_user_context_menu_items">
246 <?php do_action('fluent_community/before_header_right_menu_items', $auth); ?>
247 <?php if ($has_color_scheme): ?>
248 <li class="top_menu_item fcom_color_mode">
249 <button class="fcom_color_mode_core fcom_mode_switch fcom_menu_button">
250 <span class="fcom_color_mode_action el-icon">
251 <svg class="show_on_light" width="20" height="20" viewBox="0 0 20 20" fill="none"
252 xmlns="http://www.w3.org/2000/svg">
253 <path
254 d="M17.9163 11.7317C16.9166 12.2654 15.7748 12.5681 14.5623 12.5681C10.6239 12.5681 7.43128 9.37543 7.43128 5.43705C7.43128 4.22456 7.73388 3.08274 8.2677 2.08301C4.72272 2.91382 2.08301 6.09562 2.08301 9.89393C2.08301 14.3246 5.67476 17.9163 10.1054 17.9163C13.9038 17.9163 17.0855 15.2767 17.9163 11.7317Z"
255 stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
256 stroke-linejoin="round"/>
257 </svg>
258 <svg class="show_on_dark" width="20" height="20" viewBox="0 0 20 20" fill="none"
259 xmlns="http://www.w3.org/2000/svg">
260 <g clip-path="url(#clip0_2906_20675)">
261 <path
262 d="M14.1663 10.0007C14.1663 12.3018 12.3009 14.1673 9.99967 14.1673C7.69849 14.1673 5.83301 12.3018 5.83301 10.0007C5.83301 7.69946 7.69849 5.83398 9.99967 5.83398C12.3009 5.83398 14.1663 7.69946 14.1663 10.0007Z"
263 stroke="currentColor" stroke-width="1.5"/>
264 <path
265 d="M9.99984 1.66699V2.91699M9.99984 17.0837V18.3337M15.8922 15.8931L15.0083 15.0092M4.99089 4.99137L4.107 4.10749M18.3332 10.0003H17.0832M2.9165 10.0003H1.6665M15.8926 4.10758L15.0087 4.99147M4.99129 15.0093L4.10741 15.8932"
266 stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
267 </g>
268 <defs>
269 <clipPath id="clip0_2906_20675">
270 <rect width="20" height="20" fill="currentColor"/>
271 </clipPath>
272 </defs>
273 </svg>
274 </span>
275 </button>
276 </li>
277 <?php endif; ?>
278 <li class="top_menu_item fcom_search_holder"></li>
279 <?php if ($auth): ?>
280 <li class="top_menu_item fcom_notification_holder fcom_countable_notification_holder">
281 <?php if ($context != 'headless'): ?>
282 <?php
283 $notificationCount = NotificationSubscriber::unread()
284 ->where('user_id', $auth->user_id)
285 ->count();
286 ?>
287 <a href="<?php echo esc_url(Helper::baseUrl('/notifications')); ?>" type="button"
288 class="fcom_menu_button fcom_theme_button">
289 <i class="el-icon">
290 <svg width="20" height="20" viewBox="0 0 20 20" fill="none"
291 xmlns="http://www.w3.org/2000/svg">
292 <path
293 d="M2.10794 11.9954C1.93073 13.1229 2.72301 13.9055 3.69305 14.2955C7.41201 15.7908 12.5873 15.7908 16.3063 14.2955C17.2763 13.9055 18.0686 13.1229 17.8914 11.9954C17.7825 11.3024 17.244 10.7254 16.845 10.162C16.3224 9.41493 16.2705 8.60009 16.2704 7.73317C16.2704 4.38291 13.4629 1.66699 9.99968 1.66699C6.53645 1.66699 3.72895 4.38291 3.72895 7.73317C3.72887 8.60009 3.67694 9.41493 3.15435 10.162C2.75537 10.7254 2.21685 11.3024 2.10794 11.9954Z"
294 stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
295 stroke-linejoin="round"></path>
296 <path
297 d="M7.5 17.5C8.16345 18.0182 9.03956 18.3333 10 18.3333C10.9604 18.3333 11.8366 18.0182 12.5 17.5"
298 stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
299 stroke-linejoin="round"></path>
300 </svg>
301 </i>
302 <?php if ($notificationCount): ?>
303 <sup
304 class="el-badge__content fcomc_unread_badge el-badge__content--danger is-fixed"><?php echo $notificationCount > 20 ? '20+' : esc_html($notificationCount); ?></sup>
305 <?php endif; ?>
306 </a>
307 <?php endif; ?>
308 </li>
309 <?php endif; ?>
310 <?php do_action('fluent_community/before_header_menu_items', $auth, $context); ?>
311 <?php if ($auth): ?>
312 <li class="top_menu_item fcom_menu_item_user">
313 <div class="fcom_user_menu_item">
314 <div class="fcom_profile_extend">
315 <div class="fcom_profile_menu">
316 <div class="user_avatar">
317 <img alt="User Photo" src="<?php echo esc_url($auth->avatar); ?>"/>
318 <span class="avatar_icon">
319 <svg xmlns="http://www.w3.org/2000/svg" height="8" width="8"
320 viewBox="0 0 1024 1024">
321 <path fill="currentColor"
322 d="M104.704 338.752a64 64 0 0 1 90.496 0l316.8 316.8 316.8-316.8a64 64 0 0 1 90.496 90.496L557.248 791.296a64 64 0 0 1-90.496 0L104.704 429.248a64 64 0 0 1 0-90.496z"></path>
323 </svg>
324 </span>
325 </div>
326 <span class="user_name">
327 <?php echo esc_html($auth->display_name); ?>
328 </span>
329 <ul class="fcom_profile_sub_menu">
330 <li class="fcom_profile_block">
331 <a href="<?php echo esc_url(Helper::baseUrl('/u/' . $auth->username)); ?>">
332 <div class="fcom_profile_avatar">
333 <img src="<?php echo esc_url($auth->avatar); ?>"
334 alt="<?php echo esc_attr($auth->display_name); ?>">
335 </div>
336 <div class="fcom_user_info">
337 <p class="fcom_user_name"><?php echo esc_html($auth->display_name); ?></p>
338 <p class="fcom_user_email"><?php echo esc_html($auth->user->user_email); ?></p>
339 </div>
340 </a>
341 </li>
342 <?php foreach ($profileLinks as $link): ?>
343 <li>
344 <?php Helper::renderLink($link, 'fcom_menu_link', ''); ?>
345 </li>
346 <?php endforeach; ?>
347 </ul>
348 </div>
349 </div>
350 </li>
351 <?php else: ?>
352 <?php
353 if (AuthHelper::isRegistrationEnabled()) {
354 $btnText = __('Login / Signup', 'fluent-community');
355 } else {
356 $btnText = __('Login', 'fluent-community');
357 }
358 ?>
359
360 <li style="margin-right: 5px" class="top_menu_item fcom_login_li">
361 <a class="fcom_login_btn el-button fcom_primary_button" href="<?php echo esc_url($auth_url); ?>">
362 <span><?php echo esc_html($btnText); ?></span>
363 </a>
364 </li>
365 <?php endif; ?>
366 <?php do_action('fluent_community/after_header_right_menu_items', $auth); ?>
367 </ul>
368 <?php
369 }
370
371 public function appVars()
372 {
373 $userModel = User::where('ID', get_current_user_id())->first();
374 $xprofile = null;
375
376 if ($userModel) {
377 $xprofile = XProfile::where('user_id', get_current_user_id())->first();
378 if (!$xprofile) {
379 $xprofile = $userModel->syncXProfile();
380 }
381 }
382
383 $authData = null;
384 $spaceSlugs = [];
385 $spaceGroups = [];
386
387 if ($userModel && $xprofile) {
388 $wpUser = get_user_by('ID', $userModel->ID);
389
390 if ($userModel->isCommunityModerator()) {
391 $userSpaces = Space::orderBy('serial', 'ASC')->get();
392 $spaceGroups = SpaceGroup::orderBy('serial', 'ASC')
393 ->select(['id', 'title', 'slug', 'settings'])
394 ->get();
395 } else {
396 $userSpaces = Space::orderBy('serial', 'ASC')
397 ->whereHas('members', function ($q) {
398 $q->where('user_id', get_current_user_id())
399 ->where('status', 'active');
400 })
401 ->get();
402
403 $userSpaceParentIds = $userSpaces->pluck('parent_id')->filter()->unique()->toArray();
404
405 $spaceGroups = SpaceGroup::orderBy('serial', 'ASC')
406 ->select(['id', 'title', 'slug', 'settings'])
407 ->whereIn('id', $userSpaceParentIds)
408 ->get();
409 }
410
411 BaseSpace::preloadMemberships($userSpaces, $userModel->ID);
412
413 $userSpaces->each(function ($space) use ($userModel) {
414 $space = $space->formatSpaceData($userModel);
415 do_action_ref_array('fluent_community/space', [&$space]);
416 });
417
418 $userSpaces = $userSpaces->keyBy('slug');
419
420 $userModel->cacheAccessSpaces();
421
422 $spaceSlugs = $userSpaces->pluck('slug')->toArray();
423
424 $authData = [
425 'id' => $xprofile->user_id,
426 'user_id' => $xprofile->user_id,
427 'username' => $xprofile->username,
428 'display_name' => $xprofile->display_name,
429 'first_name' => $wpUser->first_name ? $wpUser->first_name : $xprofile->getFirstName(),
430 'avatar' => $xprofile->avatar,
431 'total_points' => $xprofile->total_points,
432 'last_activity' => $xprofile->last_activity,
433 'email' => $userModel->user_email,
434 'spaces' => $userSpaces,
435 'community_roles' => $userModel->getCommunityRoles(),
436 'is_verified' => $xprofile->is_verified,
437 'compilation_score' => $xprofile->getCompletionScore(),
438 'meta' => $xprofile->meta
439 ];
440 } else {
441 $authData = [
442 'id' => 0,
443 'user_id' => 0,
444 'spaces' => (object)[],
445 'community_roles' => [],
446 'compilation_score' => 100
447 ];
448 }
449
450 $settings = Helper::generalSettings();
451
452 $onboardSettings = Helper::getOnboardingSettings();
453
454 global $wp_version;
455
456 $isRtl = Helper::isRtl();
457
458 $isAbsoluteUrl = defined('FLUENT_COMMUNITY_PORTAL_SLUG') && FLUENT_COMMUNITY_PORTAL_SLUG == '';
459
460
461 $editorFrameUrl = site_url('?fluent_community_block_editor=1');
462
463 if (current_user_can('edit_posts')) {
464 $editorFrameUrl = admin_url('edit.php?fluent_community_block_editor=1');
465 }
466
467 $portalVars = apply_filters('fluent_community/portal_vars', [
468 'portal_notices' => apply_filters('fluent_community/portal_notices', []),
469 'i18n' => TransStrings::getStrings(),
470 'auth' => $authData,
471 'ajaxurl' => admin_url('admin-ajax.php'),
472 'ajax_nonce' => wp_create_nonce('fluent_community_ajax_nonce'),
473 'slug' => 'fluent-community',
474 'rest' => $this->getRestInfo(),
475 'user_id' => $userModel ? $userModel->ID : null,
476 'assets_url' => FLUENT_COMMUNITY_PLUGIN_URL . 'assets/',
477 'permissions' => $userModel ? $userModel->getPermissions() : ['read' => true],
478 'logo' => Arr::get($settings, 'logo'),
479 'site_title' => Arr::get($settings, 'site_title'),
480 'access_level' => Arr::get($settings, 'access.acess_level'),
481 'user_membership_slugs' => $spaceSlugs,
482 'block_editor_assets' => [
483 'scripts' => [
484 'react' => Vite::getStaticSrcUrl('libs/isolated-editor/react.production.min.js'),
485 'react-dom' => Vite::getStaticSrcUrl(
486 'libs/isolated-editor/react-dom.production.min.js'
487 ),
488 'isolated-block-editor' => Vite::getStaticSrcUrl('libs/isolated-editor/isolated-block-editor.min.js')
489 ],
490 'styles' => [
491 $isRtl ? Vite::getStaticSrcUrl('libs/isolated-editor/core.rtl.css') : Vite::getStaticSrcUrl('libs/isolated-editor/core.css'),
492 $isRtl ? Vite::getStaticSrcUrl('libs/isolated-editor/isolated-block-editor.rtl.css') : Vite::getStaticSrcUrl('libs/isolated-editor/isolated-block-editor.css'),
493 includes_url('css/dist/block-editor/content.min.css?ver=' . $wp_version),
494 ]
495 ],
496 'features' => [
497 'disable_global_posts' => Arr::get($settings, 'disable_global_posts', '') == 'yes',
498 'has_survey_poll' => true,
499 'is_onboarding_enabled' => Arr::get($onboardSettings, 'is_onboarding_enabled', 'no') == 'yes',
500 'can_switch_layout' => Utility::getCustomizationSetting('disable_feed_layout') !== 'yes',
501 'mention_mail' => Utility::hasEmailAnnouncementEnabled(),
502 'max_media_per_post' => apply_filters('fluent_community/max_media_per_post', Utility::getCustomizationSetting('max_media_per_post')),
503 'has_post_title' => Utility::postTitlePref(),
504 'has_course' => Helper::isFeatureEnabled('course_module'),
505 'followers_module' => Helper::isFeatureEnabled('followers_module'),
506 'skicky_sidebar' => Utility::isCustomizationEnabled('fixed_sidebar'),
507 'post_layout' => Utility::getCustomizationSetting('rich_post_layout'),
508 'member_list_layout' => Utility::getCustomizationSetting('member_list_layout'),
509 'default_feed_layout' => Utility::getCustomizationSetting('default_feed_layout'),
510 'disable_feed_sort_by' => Utility::getCustomizationSetting('disable_feed_sort_by'),
511 'default_feed_sort_by' => Utility::getCustomizationSetting('default_feed_sort_by'),
512 'video_embeder' => apply_filters('fluent_community/has_video_embeder', true),
513 'has_inline_image_upload' => apply_filters('fluent_community/has_inline_image_upload', 'yes'),
514 'has_topics' => !!Utility::getTopics(),
515 'show_post_modal' => Utility::isCustomizationEnabled('show_post_modal'),
516 'has_analytics' => Utility::hasAnalyticsEnabled(),
517 'can_deactivate_account' => Utility::getPrivacySetting('can_deactive_account') === 'yes',
518 ],
519 'route_classes' => array_filter([
520 'fcom_sticky_header' => Utility::isCustomizationEnabled('fixed_page_header'),
521 'fcom_sticky_sidebar' => Utility::isCustomizationEnabled('fixed_sidebar'),
522 'fcom_has_icon_on_header_menu' => Utility::isCustomizationEnabled('icon_on_header_menu')
523 ]),
524 'urls' => [
525 'site_url' => home_url(),
526 'portal_base' => Helper::baseUrl('/'),
527 'global_search' => Helper::baseUrl(),
528 ],
529 'last_feed_id' => FeedsHelper::getLastFeedId(),
530 'unread_notification_count' => $userModel ? $userModel->getUnreadNotificationCount() : 0,
531 'unread_feed_ids' => $userModel ? $userModel->getUnreadNotificationFeedIds() : [],
532 'date_offset' => time() - current_time('timestamp'),
533 'date_formatter' => Helper::getDateFormatter(true),
534 'time_formatter' => Helper::getTimeFormatter(true),
535 'date_time_formatter' => Helper::getDateFormatter(true) . ' ' . Helper::getTimeFormatter(true),
536 'portal_slug' => Helper::getPortalSlug(true),
537 'socialLinkProviders' => ProfileHelper::socialLinkProviders(true),
538 'space_groups' => $spaceGroups,
539 'mobileMenuItems' => Helper::getMobileMenuItems(),
540 'feed_links' => Helper::getEnabledFeedLinks(),
541 'post_order_by_options' => Helper::getPostOrderOptions('feed'),
542 'comment_order_by_options' => Helper::getCommentOrderOptions('comment'),
543 'user_post_order_by_options' => Helper::getPostOrderOptions('user'),
544 'routing_system' => Helper::getPortalRouteType(),
545 'portal_url' => Helper::baseUrl('/'),
546 'upgrade_url' => 'https://fluentcommunity.co/discount-deal/?utm_source=wp&utm_medium=upgrade&utm_campaign=upgrade',
547 'dateTime18n' => apply_filters('fluent_community/date_time_i18n', [
548 /* translators: weekday. Please keep the serial and format */
549 'weekdays' => __('Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday', 'fluent-community'),
550 /* translators: Months Please keep the serial and format*/
551 'months' => __('January_February_March_April_May_June_July_August_September_October_November_December', 'fluent-community'),
552 /* translators: weekday short Please keep the serial and format*/
553 'weekdaysShort' => __('Sun_Mon_Tue_Wed_Thu_Fri_Sat', 'fluent-community'),
554 /* translators: Months short Please keep the serial and format*/
555 'monthsShort' => __('Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec', 'fluent-community'),
556 /* translators: weekday min Please keep the serial and format*/
557 'weekdaysMin' => __('Su_Mo_Tu_We_Th_Fr_Sa', 'fluent-community'),
558 'relativeTime' => [
559 /* translators: Relative Date Formats. Please do not alter %s*/
560 'future' => __('in %s', 'fluent-community'),
561 /* translators: Relative Date Formats. Please do not alter %s*/
562 'past' => __('%s ago', 'fluent-community'),
563 /* translators: Relative Date Formats.*/
564 's' => __('a few seconds', 'fluent-community'),
565 /* translators: Relative Date Formats.*/
566 'm' => __('a minute', 'fluent-community'),
567 /* translators: Relative Date Formats. Don't alter %d*/
568 'mm' => __('%d minutes', 'fluent-community'),
569 /* translators: Relative Date Formats*/
570 'h' => __('an hour', 'fluent-community'),
571 /* translators: Relative Date Formats. Don't alter %d*/
572 'hh' => __('%d hours', 'fluent-community'),
573 /* translators: Relative Date Formats*/
574 'd' => __('a day', 'fluent-community'),
575 /* translators: Relative Date Formats. Don't alter %d*/
576 'dd' => __('%d days', 'fluent-community'),
577 /* translators: Relative Date Formats*/
578 'M' => __('a month', 'fluent-community'),
579 /* translators: Relative Date Formats. Don't alter %d*/
580 'MM' => __('%d months', 'fluent-community'),
581 /* translators: Relative Date Formats*/
582 'y' => __('a year', 'fluent-community'),
583 /* translators: Relative Date Formats. Don't alter %d*/
584 'yy' => __('%d years', 'fluent-community')
585 ],
586 'relativeTimeMobile' => [
587 /* translators: Relative Date Formats. Please do not alter %s*/
588 'future' => __('in %s', 'fluent-community'),
589 /* translators: Relative Date Formats. Please do not alter %s*/
590 'past' => __('%s ago', 'fluent-community'),
591 /* translators: Relative Date Formats.*/
592 's' => __('few sec', 'fluent-community'),
593 /* translators: Relative Date Formats.*/
594 'm' => __('1min', 'fluent-community'),
595 /* translators: Relative Date Formats. Don't alter %d*/
596 'mm' => __('%dmin', 'fluent-community'),
597 /* translators: Relative Date Formats*/
598 'h' => __('1h', 'fluent-community'),
599 /* translators: Relative Date Formats. Don't alter %d*/
600 'hh' => __('%dh', 'fluent-community'),
601 /* translators: Relative Date Formats*/
602 'd' => __('1d', 'fluent-community'),
603 /* translators: Relative Date Formats. Don't alter %d*/
604 'dd' => __('%dd', 'fluent-community'),
605 /* translators: Relative Date Formats*/
606 'M' => __('1mo', 'fluent-community'),
607 /* translators: Relative Date Formats. Don't alter %d*/
608 'MM' => __('%dmo', 'fluent-community'),
609 /* translators: Relative Date Formats*/
610 'y' => __('1y', 'fluent-community'),
611 /* translators: Relative Date Formats. Don't alter %d*/
612 'yy' => __('%dy', 'fluent-community')
613 ]
614 ]),
615 'topicsConfig' => Helper::getTopicsConfig(),
616 'moderationConfig' => Helper::getModerationConfig(),
617 'is_absolute_url' => $isAbsoluteUrl,
618 'portal_paths' => $isAbsoluteUrl ? Helper::portalRoutePaths() : [],
619 'suggestedColors' => Utility::getSuggestedColors(),
620 'view_leaderboard_members' => Utility::canViewLeaderboardMembers(),
621 'report_reasons' => Helper::getReportReasons(),
622 'el_i18n' => [
623 'pagination' => [
624 /* translators: %s is replaced by the page number */
625 'currentPage' => \sprintf(__('page %s', 'fluent-community'), '{pager}'),
626 'deprecationWarning' => 'Deprecated usages detected',
627 'goto' => __('Go to', 'fluent-community'),
628 'next' => __('Go to next page', 'fluent-community'),
629 /* translators: %s is replaced by the number of pages */
630 'nextPages' => \sprintf(__('Next %s pages', 'fluent-community'), ' {pager}'),
631 'page' => __('Page', 'fluent-community'),
632 'pageClassifier' => '',
633 'pagesize' => __('/page', 'fluent-community'),
634 'prev' => __('Go to previous page', 'fluent-community'),
635 /* translators: %s is replaced by the number of pages */
636 'prevPages' => \sprintf(__('Previous %s pages', 'fluent-community'), '{pager}'),
637 /* translators: %s is replaced by the total number of items */
638 'total' => \sprintf(__('Total %s', 'fluent-community'), '{total}'),
639 ],
640 'table' => [
641 'clearFilter' => __('All', 'fluent-community'),
642 'confirmFilter' => __('Confirm', 'fluent-community'),
643 'emptyText' => __('No Data', 'fluent-community'),
644 'resetFilter' => __('Reset', 'fluent-community'),
645 'sumText' => __('Sum', 'fluent-community'),
646 ],
647 'image' => [
648 'error' => __('Failed to Load', 'fluent-community'),
649 ],
650 'upload' => [
651 'continue' => __('Continue', 'fluent-community'),
652 'delete' => __('Delete', 'fluent-community'),
653 'deleteTip' => __('press delete to remove', 'fluent-community'),
654 'preview' => __('Preview', 'fluent-community'),
655 ],
656 'select' => [
657 'loading' => __('Loading', 'fluent-community'),
658 'noData' => __('No data', 'fluent-community'),
659 'noMatch' => __('No matching data', 'fluent-community'),
660 'placeholder' => __('Select', 'fluent-community'),
661 ],
662 'datepicker' => [
663 'now' => __('Now', 'fluent-community'),
664 'today' => __('Today', 'fluent-community'),
665 'cancel' => __('Cancel', 'fluent-community'),
666 'clear' => __('Clear', 'fluent-community'),
667 'confirm' => __('OK', 'fluent-community'),
668 'dateTablePrompt' => __('Use the arrow keys and enter to select the day of the month', 'fluent-community'),
669 'monthTablePrompt' => __('Use the arrow keys and enter to select the month', 'fluent-community'),
670 'yearTablePrompt' => __('Use the arrow keys and enter to select the year', 'fluent-community'),
671 'selectedDate' => __('Selected date', 'fluent-community'),
672 'selectDate' => __('Select date', 'fluent-community'),
673 'selectTime' => __('Select time', 'fluent-community'),
674 'startDate' => __('Start Date', 'fluent-community'),
675 'startTime' => __('Start Time', 'fluent-community'),
676 'endDate' => __('End Date', 'fluent-community'),
677 'endTime' => __('End Time', 'fluent-community'),
678 'prevYear' => __('Previous Year', 'fluent-community'),
679 'nextYear' => __('Next Year', 'fluent-community'),
680 'prevMonth' => __('Previous Month', 'fluent-community'),
681 'nextMonth' => __('Next Month', 'fluent-community'),
682 'year' => __('year', 'fluent-community'),
683 'month1' => __('January', 'fluent-community'),
684 'month2' => __('February', 'fluent-community'),
685 'month3' => __('March', 'fluent-community'),
686 'month4' => __('April', 'fluent-community'),
687 'month5' => __('May', 'fluent-community'),
688 'month6' => __('June', 'fluent-community'),
689 'month7' => __('July', 'fluent-community'),
690 'month8' => __('August', 'fluent-community'),
691 'month9' => __('September', 'fluent-community'),
692 'month10' => __('October', 'fluent-community'),
693 'month11' => __('November', 'fluent-community'),
694 'month12' => __('December', 'fluent-community'),
695 'week' => __('week', 'fluent-community'),
696 'weeks' => [
697 'sun' => __('Sun', 'fluent-community'),
698 'mon' => __('Mon', 'fluent-community'),
699 'tue' => __('Tue', 'fluent-community'),
700 'wed' => __('Wed', 'fluent-community'),
701 'thu' => __('Thu', 'fluent-community'),
702 'fri' => __('Fri', 'fluent-community'),
703 'sat' => __('Sat', 'fluent-community'),
704 ],
705 'weeksFull' => [
706 'sun' => __('Sunday', 'fluent-community'),
707 'mon' => __('Monday', 'fluent-community'),
708 'tue' => __('Tuesday', 'fluent-community'),
709 'wed' => __('Wednesday', 'fluent-community'),
710 'thu' => __('Thursday', 'fluent-community'),
711 'fri' => __('Friday', 'fluent-community'),
712 'sat' => __('Saturday', 'fluent-community'),
713 ],
714 'months' => [
715 'jan' => __('Jan', 'fluent-community'),
716 'feb' => __('Feb', 'fluent-community'),
717 'mar' => __('Mar', 'fluent-community'),
718 'apr' => __('Apr', 'fluent-community'),
719 'may' => __('May', 'fluent-community'),
720 'jun' => __('Jun', 'fluent-community'),
721 'jul' => __('Jul', 'fluent-community'),
722 'aug' => __('Aug', 'fluent-community'),
723 'sep' => __('Sep', 'fluent-community'),
724 'oct' => __('Oct', 'fluent-community'),
725 'nov' => __('Nov', 'fluent-community'),
726 'dec' => __('Dec', 'fluent-community'),
727 ],
728 ]
729 ],
730 'course_sections_collapsed' => apply_filters('fluent_community/course_section_collapse_default', 'no'),
731 'course_lesson_fullscreen' => apply_filters('fluent_community/course_lesson_fullscreen_default', 'no'),
732 'default_profile_tab' => apply_filters('fluent_community/default_profile_tab_route', ''),
733 'default_post_space' => apply_filters('fluent_community/create_post_default_space', ''),
734 'wp_lesson_editor_frame' => $editorFrameUrl,
735 'has_fluentcrm' => defined('FLUENTCRM'),
736 'lazy_styles' => [
737 'wp-block-library-css' => includes_url('css/dist/block-library/style.min.css?version=' . $wp_version),
738 'fcom-block-content-styling-css' => FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/content_styling.css?version=' . FLUENT_COMMUNITY_PLUGIN_VERSION
739 ],
740 'debug_mode' => 1
741 ]);
742
743 $portalVars['welcome_banner'] = Helper::getWelcomeBanner($userModel ? 'login' : 'logout');
744
745 if (!$xprofile) {
746 $portalVars['auth_url'] = $this->getAuthUrl();
747 $portalVars['allow_signup'] = !!AuthHelper::isRegistrationEnabled();
748 }
749
750 return $portalVars;
751 }
752
753 public function getGlobalScriptVars($scope = 'wp')
754 {
755 return apply_filters('fluent_community/general_portal_vars', [
756 'scope' => $scope,
757 'theme' => get_option('template'),
758 'default_color' => 'light',
759 'color_switch_cookie_name' => '',
760 'has_color_scheme' => Helper::hasColorScheme(),
761 'collapse_sidebar_groups' => Utility::isCustomizationEnabled('collapse_sidebar_groups'),
762 'hide_header_on_scroll' => Utility::isCustomizationEnabled('hide_header_on_scroll'),
763 ]);
764 }
765
766 protected function getRestInfo()
767 {
768 $app = fluentCommunityApp();
769 $ns = $app->config->get('app.rest_namespace');
770 $v = $app->config->get('app.rest_version');
771
772 return [
773 'base_url' => esc_url_raw(rest_url()),
774 'url' => rest_url($ns . '/' . $v),
775 'nonce' => wp_create_nonce('wp_rest'),
776 'namespace' => $ns,
777 'version' => $v,
778 ];
779 }
780
781 protected function renderFullApp()
782 {
783 do_action('litespeed_control_set_nocache', 'fluentcommunity api request'); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
784 // set no cache headers
785 nocache_headers();
786
787 if (isset($_REQUEST['fcom_action'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
788 $action = sanitize_text_field(wp_unslash($_REQUEST['fcom_action'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
789 do_action('fluent_community/portal_action_' . $action, $_GET); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
790 }
791
792 $pathParts = explode('/', $this->currentPath);
793 do_action('fluent_community/rendering_path_ssr_' . $pathParts[0], $pathParts);
794
795 $userId = get_current_user_id();
796
797 if (!$userId && !Helper::isPublicAccessible()) {
798 $url = home_url(add_query_arg($_REQUEST, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
799 $parsedUrl = wp_parse_url($url);
800 $redirectPath = $parsedUrl['path'] ?? '/';
801 if (!empty($parsedUrl['query'])) {
802 $redirectPath .= '?' . $parsedUrl['query'];
803 }
804 $settings = Helper::generalSettings();
805 $adminAuthUrl = Arr::get($settings, 'auth_url', '');
806
807 // Drop admin's URL if it points back into the portal (would infinite loop)
808 if ($adminAuthUrl) {
809 $authPath = wp_parse_url($adminAuthUrl, PHP_URL_PATH) ?: '';
810 $portalPath = wp_parse_url(Helper::baseUrl(), PHP_URL_PATH) ?: '';
811 if ($authPath && $portalPath && strpos(trim($authPath, '/'), trim($portalPath, '/')) === 0) {
812 $adminAuthUrl = '';
813 }
814 }
815
816 $authUrl = $adminAuthUrl ?: $this->getAuthUrl();
817
818 if ($authUrl) {
819 $authUrl = add_query_arg([
820 'redirect_to' => $redirectPath
821 ], $authUrl);
822 } else {
823 $authUrl = wp_login_url($redirectPath);
824 }
825
826 do_action('fluent_community/portal/not_logged_in', $authUrl);
827 wp_safe_redirect($authUrl);
828 exit();
829 }
830
831 do_action('fluent_community/portal/viewed');
832 $xprofile = Helper::getCurrentProfile();
833
834 if ($xprofile && $xprofile->status != 'active') {
835
836 if ($xprofile->status == 'pending') {
837 $this->viewErrorPage(__('Your request is pending', 'fluent-community'), __('An admin needs to approve your join request. Please contact an admin to get approval', 'fluent-community'));
838 } else if ($xprofile->status == '') { // it's a self deactiavted account
839 $this->viewErrorPage(__('Your account is deactivated', 'fluent-community'), __('You have deactivated your community profile.', 'fluent-community'), true, [
840 'btn_url' => Helper::baseUrl('?fcom_action=reactivate_account&__nonce=' . wp_create_nonce('fcom_reactivate_account')),
841 'btn_text' => __('Activate my account', 'fluent-community')
842 ]);
843 } else {
844 $this->viewErrorPage(__('Access denied', 'fluent-community'), __('Sorry, you cannot access this community', 'fluent-community'));
845 }
846 }
847
848 if ($xprofile) {
849 if (!Helper::canAccessPortal()) {
850 $generalSettings = Helper::generalSettings();
851 $this->viewErrorPage(__('Access Denied', 'fluent-community'), Arr::get($generalSettings, 'restricted_role_content'));
852 }
853 if ($xprofile->user) {
854 $xprofile->user->cacheAccessSpaces();
855 }
856
857 /*
858 * Just in case if username is not sanitized
859 */
860 if ($xprofile->username != CustomSanitizer::sanitizeUserName($xprofile->username)) {
861 $xprofile->username = ProfileHelper::generateUserName($xprofile->user_id);
862 $xprofile->save();
863 }
864
865 do_action('fluent_community/portal_render_for_user', $xprofile);
866 }
867
868 $data = $this->getAppData();
869 $data = $this->maybePushDynamicMetaData($data);
870
871 $data['isHeadless'] = Helper::isHeadless();
872
873 if (!$data['isHeadless']) {
874 $this->loadClassicPortalAssets($data);
875 } else {
876 do_action('fluent_community/rendering_headless_portal', $data);
877 }
878
879 do_action('fluent_community/before_portal_rendered', $data);
880
881 status_header(200);
882 App::make('view')->render('portal_page', $data);
883 exit();
884 }
885
886 public function loadClassicPortalAssets($data)
887 {
888 $cssFiles = $data['css_files'];
889
890 unset($cssFiles['fcom_theme_default']);
891
892 foreach ($cssFiles as $fileName => $cssFile) {
893 wp_enqueue_style($fileName, $cssFile['url'], [], FLUENT_COMMUNITY_PLUGIN_VERSION, 'screen');
894 }
895
896 $jsFiles = $data['js_files'];
897 $jsTags = array_keys($jsFiles);
898 add_filter('script_loader_tag', function ($tag, $handle) use ($jsTags) {
899 if (!in_array($handle, $jsTags)) {
900 return $tag;
901 }
902 $tag = str_replace(' src', ' type="module" src', $tag);
903 return $tag;
904 }, 10, 2);
905 foreach ($jsFiles as $fileName => $jsFile) {
906 wp_enqueue_script($fileName, $jsFile['url'], $jsFile['deps'], FLUENT_COMMUNITY_PLUGIN_VERSION, [
907 'in_footer' => true,
908 'strategy' => 'defer',
909 'type' => 'module'
910 ]);
911 }
912
913 foreach ($data['js_vars'] as $varKey => $values) {
914 wp_localize_script('fcom_start', $varKey, $values);
915 }
916 }
917
918 public function getAppData()
919 {
920 $isRtl = Helper::isRtl();
921
922 $userId = get_current_user_id();
923 $url = home_url(add_query_arg($_REQUEST, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
924
925 $appVars = $this->appVars();
926 $generalSettings = Helper::generalSettings();
927 $isDev = Utility::isDev();
928
929 $siteTitle = Arr::get($generalSettings, 'site_title');
930
931 $dataVars = [
932 'title' => $siteTitle,
933 'og_title' => $siteTitle,
934 'description' => get_bloginfo('description'),
935 'featured_image' => Arr::get($generalSettings, 'featured_image', ''),
936 'header_js_files' => [],
937 'js_vars' => [
938 'fluentComAdmin' => $appVars,
939 'fcom_portal_general' => $this->getGlobalScriptVars('portal'),
940 ],
941 'url' => $url,
942 'current_route' => $this->currentPath,
943 'contact' => $appVars['auth'],
944 'user' => $userId ? get_user_by('ID', $userId) : null,
945 'route_group' => Helper::getRouteNameByRequestPath($this->currentPath),
946 'is_dev' => $isDev,
947 'theme_color' => Utility::getThemeColor()
948 ];
949
950 if ($isDev) {
951 $dataVars['css_files'] = [
952 'fcom_theme_default' => [
953 'url' => Vite::getDynamicSrcUrl('theme-default.scss')
954 ],
955 'fcom_global' => [
956 'url' => Vite::getDynamicSrcUrl('global.scss')
957 ],
958 'fcom_vendor' => [
959 'url' => Vite::getDynamicSrcUrl('app_css.scss')
960 ]
961 ];
962 $dataVars['js_files'] = [
963 'fcom_general' => [
964 'url' => Vite::getDynamicSrcUrl('portal_general.js'),
965 'deps' => []
966 ],
967 'fcom_start' => [
968 'url' => Vite::getDynamicSrcUrl('start.js'),
969 'deps' => []
970 ],
971 'fcom_app' => [
972 'url' => Vite::getDynamicSrcUrl('app.js'),
973 'deps' => []
974 ]
975 ];
976
977 } else {
978 $dataVars['js_files'] = [
979 'fcom_general' => [
980 'url' => Vite::getStaticSrcUrl('portal_general.js'),
981 'deps' => []
982 ],
983 'fcom_start' => [
984 'url' => Vite::getStaticSrcUrl('start.js'),
985 'deps' => []
986 ],
987 'fcom_app' => [
988 'url' => Vite::getStaticSrcUrl('app.js'),
989 'deps' => []
990 ]
991 ];
992 $dataVars['css_files'] = [
993 'fcom_theme_default' => [
994 'url' => Vite::getStaticSrcUrl('theme-default.css', $isRtl)
995 ],
996 'fcom_global' => [
997 'url' => Vite::getStaticSrcUrl('global.css', $isRtl)
998 ],
999 'fcom_app_css' => [
1000 'url' => Vite::getStaticSrcUrl('app.css', $isRtl)
1001 ],
1002 'fcom_vendor' => [
1003 'url' => Vite::getStaticSrcUrl('app_css.css', $isRtl)
1004 ],
1005 ];
1006 }
1007
1008 return apply_filters('fluent_community/portal_data_vars', $dataVars);
1009 }
1010
1011 protected function maybePushDynamicMetaData($data)
1012 {
1013 $dynamicRoute = Arr::get($data, 'route_group');
1014
1015 $data['landing_route'] = $dynamicRoute;
1016
1017 $validGroups = [
1018 'user_profile',
1019 'community_view',
1020 'course_view',
1021 'feed_view',
1022 'lesson_view'
1023 ];
1024
1025 if (!in_array($dynamicRoute, $validGroups)) {
1026 return $data;
1027 }
1028
1029 if ($dynamicRoute == 'user_profile') {
1030 $uriParts = explode('/', $this->currentPath);
1031 if (count($uriParts) >= 2) {
1032 $userName = $uriParts[1];
1033 $xProfile = XProfile::where('username', $userName)->first();
1034 if ($xProfile) {
1035 $data['title'] = 'User ' . esc_html($xProfile->display_name) . ' - ' . $data['title'];
1036 $data['og_title'] = esc_html($xProfile->display_name) . ' - ' . $data['og_title'];
1037 if ($xProfile->short_description) {
1038 $data['description'] = Helper::getHumanExcerpt($xProfile->short_description, 100);
1039 }
1040 if ($coverPhoto = Arr::get($xProfile->meta, 'cover_photo')) {
1041 $data['featured_image'] = $coverPhoto;
1042 }
1043 }
1044 }
1045 return $data;
1046 }
1047
1048 if ($dynamicRoute == 'community_view' || $dynamicRoute == 'course_view') {
1049 $uriParts = explode('/', $this->currentPath);
1050 if (count($uriParts) >= 2) {
1051 $paceSlug = $uriParts[1];
1052 $space = BaseSpace::query()->onlyMain()->where('slug', $paceSlug)->first();
1053 if ($space && $space->privacy != 'secret') {
1054
1055 if ($dynamicRoute == 'course_view') {
1056 /* translators: %s is replaced by the title of the space */
1057 $data['title'] = sprintf(__('Enroll %s', 'fluent-community'), esc_html($space->title) . ' - ' . $data['title']);
1058 /* translators: %s is replaced by the title of the space */
1059 $data['og_title'] = sprintf(__('Enroll %s', 'fluent-community'), esc_html($space->title));
1060 } else {
1061 /* translators: %s is replaced by the title of the space */
1062 $data['title'] = sprintf(__('Join %s', 'fluent-community'), esc_html($space->title) . ' - ' . $data['title']);
1063 /* translators: %s is replaced by the title of the space */
1064 $data['og_title'] = sprintf(__('Join %s', 'fluent-community'), esc_html($space->title));
1065 }
1066
1067 if ($space->description) {
1068 $data['description'] = Helper::getHumanExcerpt($space->description, 100);
1069 }
1070 if ($ogImage = Arr::get($space->settings, 'og_image')) {
1071 $data['featured_image'] = $ogImage;
1072 } else if ($space->cover_photo) {
1073 $data['featured_image'] = $space->cover_photo;
1074 }
1075
1076 if ($dynamicRoute == 'course_view') {
1077 $data['json_ld'] = apply_filters('fluent_community/course_view_json_ld', [], $space, $data);
1078 }
1079 }
1080 }
1081 return $data;
1082 }
1083
1084 if ($dynamicRoute == 'feed_view') {
1085 $uriParts = explode('/', $this->currentPath);
1086 if (count($uriParts) >= 2) {
1087 $postSlug = end($uriParts);
1088 $feed = Feed::query()->withoutGlobalScopes()->where('slug', $postSlug)
1089 ->with([
1090 'xprofile' => function ($q) {
1091 $q->select(ProfileHelper::getXProfilePublicFields());
1092 },
1093 'space'
1094 ])
1095 ->whereDoesntHave('space', function ($q) {
1096 $q->whereIn('privacy', ['secret', 'private']);
1097 })
1098 ->first();
1099
1100 if (!$feed) {
1101 return $data;
1102 }
1103
1104 if ($feed->title) {
1105 $data['title'] = esc_html($feed->title) . ' - ' . $data['title'];
1106 $data['og_title'] = esc_html($feed->title);
1107 } else {
1108 $authorName = esc_html($feed->xprofile ? $feed->xprofile->display_name : 'Someone');
1109 $data['title'] = $authorName . ' posted at ' . $data['title'];
1110 $data['og_title'] = $authorName . ' posted at ' . $data['og_title'];
1111 }
1112
1113 $feedUrl = $feed->getPermalink();
1114 $data['canonical_url'] = $feedUrl;
1115
1116 if ($feed->message) {
1117 $data['description'] = esc_html(Helper::getHumanExcerpt($feed->message, 160));
1118 }
1119
1120 if ($mediaPreview = Arr::get($feed->meta, 'media_preview.image')) {
1121 $data['featured_image'] = esc_url($mediaPreview);
1122 } else if ($firstImage = Arr::get($feed->meta, 'media_items.0.url')) {
1123 $data['featured_image'] = esc_url($firstImage);
1124 } else if ($feed->space) {
1125 $space = $feed->space;
1126 if ($ogImage = Arr::get($space->settings, 'og_image')) {
1127 $data['featured_image'] = $ogImage;
1128 } else if ($space->cover_photo) {
1129 $data['featured_image'] = $space->cover_photo;
1130 }
1131 }
1132
1133 $data['json_ld'] = apply_filters('fluent_community/feed_view_json_ld', [], $feed, $data);
1134 }
1135 return $data;
1136 }
1137
1138 if ($dynamicRoute == 'lesson_view') {
1139 $uriParts = explode('/', $this->currentPath);
1140
1141 $lessonSlug = Arr::get($uriParts, 3);
1142 if (!$lessonSlug) {
1143 return $data;
1144 }
1145
1146 $lesson = CourseLesson::query()->where('slug', $lessonSlug)->first();
1147
1148 if (!$lesson) {
1149 return $data;
1150 }
1151
1152 $data['title'] = esc_html($lesson->title) . ' - ' . $data['title'];
1153 $data['og_title'] = esc_html($lesson->title);
1154 $data['description'] = esc_html(Helper::getHumanExcerpt($lesson->message, 120));
1155
1156 return $data;
1157 }
1158
1159 return $data;
1160 }
1161
1162 protected function getMainMenuItems($scope = 'sidebar')
1163 {
1164 $menuGroups = Helper::getMenuItemsGroup('view');
1165
1166 $items = Arr::get($menuGroups, 'mainMenuItems', []);
1167
1168 return apply_filters('fluent_community/main_menu_items', $items, $scope);
1169 }
1170
1171 public function getPortalHeader($echo = false, $context = 'headless')
1172 {
1173 $settings = Helper::generalSettings();
1174 $userId = get_current_user_id();
1175
1176 $xprofile = null;
1177 if ($userId) {
1178 $userModel = Helper::getCurrentUser();
1179 if ($userModel) {
1180 $xprofile = $userModel->syncXProfile(false);
1181 }
1182 }
1183
1184 $authUrl = $this->getAuthUrl();
1185
1186 if ($xprofile) {
1187 $xprofile->logout_url = wp_logout_url(Helper::baseUrl());
1188 }
1189
1190 $logo = Arr::get($settings, 'logo', '');
1191 $whiteLogo = Arr::get($settings, 'white_logo', '');
1192 if (!$whiteLogo) {
1193 $whiteLogo = $logo;
1194 }
1195
1196 $logoPermalink = Helper::baseUrl('/');
1197 $logoPermalinkType = Arr::get($settings, 'logo_permalink_type');
1198 if ($logoPermalinkType == 'custom') {
1199 $logoPermalink = Arr::get($settings, 'logo_permalink');
1200 }
1201
1202 $data = apply_filters('fluent_community/header_vars', [
1203 'portal_url' => Helper::baseUrl('/'),
1204 'logo' => $logo,
1205 'white_logo' => $whiteLogo,
1206 'logo_permalink' => $logoPermalink,
1207 'site_title' => Arr::get($settings, 'site_title'),
1208 'profile_url' => $xprofile ? Helper::baseUrl('u/' . $xprofile->username . '/') : '',
1209 'auth' => $xprofile ? $xprofile : null,
1210 'auth_url' => $authUrl,
1211 'menuItems' => $this->getMainMenuItems('header'),
1212 'context' => $context
1213 ]);
1214
1215 if ($echo) {
1216 App::make('view')->render('portal.header', $data);
1217 return;
1218 }
1219
1220 return (string)App::make('view')->make('portal.header', $data);
1221 }
1222
1223 private function getProfileLinks($xprofile = null)
1224 {
1225 if (!$xprofile) {
1226 $xprofile = Helper::getCurrentProfile();
1227 }
1228
1229 $profileLinks = [];
1230 $menuGroups = Helper::getMenuItemsGroup('view');
1231 if ($xprofile) {
1232 $profileLinks = Arr::get($menuGroups, 'profileDropdownItems', []);
1233
1234 $replaces = [
1235 '#{{user_url}}' => Helper::baseUrl('u/' . $xprofile->username),
1236 '#user_url' => Helper::baseUrl('u/' . $xprofile->username),
1237 '#{{logout_url}}' => wp_logout_url(Helper::baseUrl()),
1238 '#logout_url' => wp_logout_url(Helper::baseUrl())
1239 ];
1240
1241 foreach ($profileLinks as &$link) {
1242 $slug = Arr::get($link, 'slug', '');
1243
1244 if ($slug == 'my_spaces') {
1245 $link['permalink'] = Helper::baseUrl('u/' . $xprofile->username . '/spaces');
1246 } else if ($slug == 'logout') {
1247 $link['permalink'] = wp_logout_url(Helper::baseUrl());
1248 } else if ($slug == 'profile') {
1249 $link['permalink'] = Helper::baseUrl('u/' . $xprofile->username);
1250 }
1251
1252 $link['permalink'] = str_replace(array_keys($replaces), array_values($replaces), $link['permalink']);
1253 }
1254 }
1255
1256 return $profileLinks;
1257 }
1258
1259 public function getPortalSidebar($echo = false, $context = 'headless')
1260 {
1261 $data = Utility::getPortalSidebarData('sidebar');
1262
1263 if ($echo) {
1264 App::make('view')->render('portal.main_sidebar', $data);
1265 return;
1266 }
1267
1268 return (string)App::make('view')->make('portal.main_sidebar', $data);
1269 }
1270
1271 protected function getAuthUrl()
1272 {
1273 return apply_filters('fluent_community/auth/login_url', Helper::getAuthUrl());
1274 }
1275
1276 public function viewErrorPage($title, $message = '', $showBtn = true, $btnData = [])
1277 {
1278 $data = [
1279 'title' => $title,
1280 'description' => $message,
1281 'btn_txt' => Arr::get($btnData, 'btn_text', __('Go to home page', 'fluent-community')),
1282 'url' => Arr::get($btnData, 'btn_url', home_url())
1283 ];
1284
1285 if (!$showBtn) {
1286 $data['btn_txt'] = '';
1287 }
1288
1289 status_header(200);
1290 App::make('view')->render('error_page', $data);
1291 exit();
1292 }
1293 }
1294